2 * method-to-ir.c: Convert CIL to the JIT internal representation
5 * Paolo Molaro (lupus@ximian.com)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2002 Ximian, Inc.
9 * Copyright 2003-2010 Novell, Inc (http://www.novell.com)
10 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
28 #ifdef HAVE_SYS_TIME_H
36 #include <mono/utils/memcheck.h>
38 #include <mono/metadata/abi-details.h>
39 #include <mono/metadata/assembly.h>
40 #include <mono/metadata/attrdefs.h>
41 #include <mono/metadata/loader.h>
42 #include <mono/metadata/tabledefs.h>
43 #include <mono/metadata/class.h>
44 #include <mono/metadata/object.h>
45 #include <mono/metadata/exception.h>
46 #include <mono/metadata/opcodes.h>
47 #include <mono/metadata/mono-endian.h>
48 #include <mono/metadata/tokentype.h>
49 #include <mono/metadata/tabledefs.h>
50 #include <mono/metadata/marshal.h>
51 #include <mono/metadata/debug-helpers.h>
52 #include <mono/metadata/mono-debug.h>
53 #include <mono/metadata/mono-debug-debugger.h>
54 #include <mono/metadata/gc-internals.h>
55 #include <mono/metadata/security-manager.h>
56 #include <mono/metadata/threads-types.h>
57 #include <mono/metadata/security-core-clr.h>
58 #include <mono/metadata/profiler-private.h>
59 #include <mono/metadata/profiler.h>
60 #include <mono/metadata/monitor.h>
61 #include <mono/metadata/debug-mono-symfile.h>
62 #include <mono/utils/mono-compiler.h>
63 #include <mono/utils/mono-memory-model.h>
64 #include <mono/utils/mono-error-internals.h>
65 #include <mono/metadata/mono-basic-block.h>
66 #include <mono/metadata/reflection-internals.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 METHOD_ACCESS_FAILURE(method, cmethod) do { \
94 method_access_failure ((cfg), (method), (cmethod)); \
95 goto exception_exit; \
97 #define FIELD_ACCESS_FAILURE(method, field) do { \
98 field_access_failure ((cfg), (method), (field)); \
99 goto exception_exit; \
101 #define GENERIC_SHARING_FAILURE(opcode) do { \
102 if (cfg->gshared) { \
103 gshared_failure (cfg, opcode, __FILE__, __LINE__); \
104 goto exception_exit; \
107 #define GSHAREDVT_FAILURE(opcode) do { \
108 if (cfg->gsharedvt) { \
109 gsharedvt_failure (cfg, opcode, __FILE__, __LINE__); \
110 goto exception_exit; \
113 #define OUT_OF_MEMORY_FAILURE do { \
114 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
115 mono_error_set_out_of_memory (&cfg->error, ""); \
116 goto exception_exit; \
118 #define DISABLE_AOT(cfg) do { \
119 if ((cfg)->verbose_level >= 2) \
120 printf ("AOT disabled: %s:%d\n", __FILE__, __LINE__); \
121 (cfg)->disable_aot = TRUE; \
123 #define LOAD_ERROR do { \
124 break_on_unverified (); \
125 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); \
126 goto exception_exit; \
129 #define TYPE_LOAD_ERROR(klass) do { \
130 cfg->exception_ptr = klass; \
134 #define CHECK_CFG_ERROR do {\
135 if (!mono_error_ok (&cfg->error)) { \
136 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
137 goto mono_error_exit; \
141 /* Determine whenever 'ins' represents a load of the 'this' argument */
142 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
144 static int ldind_to_load_membase (int opcode);
145 static int stind_to_store_membase (int opcode);
147 int mono_op_to_op_imm (int opcode);
148 int mono_op_to_op_imm_noemul (int opcode);
150 MONO_API MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
152 static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
153 guchar *ip, guint real_offset, gboolean inline_always);
155 emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp);
157 /* helper methods signatures */
158 static MonoMethodSignature *helper_sig_domain_get;
159 static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
160 static MonoMethodSignature *helper_sig_llvmonly_imt_thunk;
163 /* type loading helpers */
164 static GENERATE_GET_CLASS_WITH_CACHE (runtime_helpers, System.Runtime.CompilerServices, RuntimeHelpers)
165 static GENERATE_TRY_GET_CLASS_WITH_CACHE (debuggable_attribute, System.Diagnostics, DebuggableAttribute)
168 * Instruction metadata
176 #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
177 #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
183 #if SIZEOF_REGISTER == 8 && SIZEOF_REGISTER == SIZEOF_VOID_P
188 /* keep in sync with the enum in mini.h */
191 #include "mini-ops.h"
196 #define MINI_OP(a,b,dest,src1,src2) ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0)),
197 #define MINI_OP3(a,b,dest,src1,src2,src3) ((src3) != NONE ? 3 : ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0))),
199 * This should contain the index of the last sreg + 1. This is not the same
200 * as the number of sregs for opcodes like IA64_CMP_EQ_IMM.
202 const gint8 ins_sreg_counts[] = {
203 #include "mini-ops.h"
208 #define MONO_INIT_VARINFO(vi,id) do { \
209 (vi)->range.first_use.pos.bid = 0xffff; \
215 mono_alloc_ireg (MonoCompile *cfg)
217 return alloc_ireg (cfg);
221 mono_alloc_lreg (MonoCompile *cfg)
223 return alloc_lreg (cfg);
227 mono_alloc_freg (MonoCompile *cfg)
229 return alloc_freg (cfg);
233 mono_alloc_preg (MonoCompile *cfg)
235 return alloc_preg (cfg);
239 mono_alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
241 return alloc_dreg (cfg, stack_type);
245 * mono_alloc_ireg_ref:
247 * Allocate an IREG, and mark it as holding a GC ref.
250 mono_alloc_ireg_ref (MonoCompile *cfg)
252 return alloc_ireg_ref (cfg);
256 * mono_alloc_ireg_mp:
258 * Allocate an IREG, and mark it as holding a managed pointer.
261 mono_alloc_ireg_mp (MonoCompile *cfg)
263 return alloc_ireg_mp (cfg);
267 * mono_alloc_ireg_copy:
269 * Allocate an IREG with the same GC type as VREG.
272 mono_alloc_ireg_copy (MonoCompile *cfg, guint32 vreg)
274 if (vreg_is_ref (cfg, vreg))
275 return alloc_ireg_ref (cfg);
276 else if (vreg_is_mp (cfg, vreg))
277 return alloc_ireg_mp (cfg);
279 return alloc_ireg (cfg);
283 mono_type_to_regmove (MonoCompile *cfg, MonoType *type)
288 type = mini_get_underlying_type (type);
290 switch (type->type) {
303 case MONO_TYPE_FNPTR:
305 case MONO_TYPE_CLASS:
306 case MONO_TYPE_STRING:
307 case MONO_TYPE_OBJECT:
308 case MONO_TYPE_SZARRAY:
309 case MONO_TYPE_ARRAY:
313 #if SIZEOF_REGISTER == 8
319 return cfg->r4fp ? OP_RMOVE : OP_FMOVE;
322 case MONO_TYPE_VALUETYPE:
323 if (type->data.klass->enumtype) {
324 type = mono_class_enum_basetype (type->data.klass);
327 if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
330 case MONO_TYPE_TYPEDBYREF:
332 case MONO_TYPE_GENERICINST:
333 type = &type->data.generic_class->container_class->byval_arg;
337 g_assert (cfg->gshared);
338 if (mini_type_var_is_vt (type))
341 return mono_type_to_regmove (cfg, mini_get_underlying_type (type));
343 g_error ("unknown type 0x%02x in type_to_regstore", type->type);
349 mono_print_bb (MonoBasicBlock *bb, const char *msg)
354 printf ("\n%s %d: [IN: ", msg, bb->block_num);
355 for (i = 0; i < bb->in_count; ++i)
356 printf (" BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn);
358 for (i = 0; i < bb->out_count; ++i)
359 printf (" BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn);
361 for (tree = bb->code; tree; tree = tree->next)
362 mono_print_ins_index (-1, tree);
366 mono_create_helper_signatures (void)
368 helper_sig_domain_get = mono_create_icall_signature ("ptr");
369 helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr");
370 helper_sig_llvmonly_imt_thunk = mono_create_icall_signature ("ptr ptr 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 method_access_failure (MonoCompile *cfg, MonoMethod *method, MonoMethod *cil_method)
383 char *method_fname = mono_method_full_name (method, TRUE);
384 char *cil_method_fname = mono_method_full_name (cil_method, TRUE);
385 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
386 mono_error_set_generic_error (&cfg->error, "System", "MethodAccessException", "Method `%s' is inaccessible from method `%s'\n", cil_method_fname, method_fname);
387 g_free (method_fname);
388 g_free (cil_method_fname);
391 static MONO_NEVER_INLINE void
392 field_access_failure (MonoCompile *cfg, MonoMethod *method, MonoClassField *field)
394 char *method_fname = mono_method_full_name (method, TRUE);
395 char *field_fname = mono_field_full_name (field);
396 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
397 mono_error_set_generic_error (&cfg->error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
398 g_free (method_fname);
399 g_free (field_fname);
402 static MONO_NEVER_INLINE void
403 inline_failure (MonoCompile *cfg, const char *msg)
405 if (cfg->verbose_level >= 2)
406 printf ("inline failed: %s\n", msg);
407 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INLINE_FAILED);
410 static MONO_NEVER_INLINE void
411 gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
413 if (cfg->verbose_level > 2) \
414 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);
415 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
418 static MONO_NEVER_INLINE void
419 gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line)
421 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);
422 if (cfg->verbose_level >= 2)
423 printf ("%s\n", cfg->exception_message);
424 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
428 * When using gsharedvt, some instatiations might be verifiable, and some might be not. i.e.
429 * foo<T> (int i) { ldarg.0; box T; }
431 #define UNVERIFIED do { \
432 if (cfg->gsharedvt) { \
433 if (cfg->verbose_level > 2) \
434 printf ("gsharedvt method failed to verify, falling back to instantiation.\n"); \
435 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
436 goto exception_exit; \
438 break_on_unverified (); \
442 #define GET_BBLOCK(cfg,tblock,ip) do { \
443 (tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
445 if ((ip) >= end || (ip) < header->code) UNVERIFIED; \
446 NEW_BBLOCK (cfg, (tblock)); \
447 (tblock)->cil_code = (ip); \
448 ADD_BBLOCK (cfg, (tblock)); \
452 #if defined(TARGET_X86) || defined(TARGET_AMD64)
453 #define EMIT_NEW_X86_LEA(cfg,dest,sr1,sr2,shift,imm) do { \
454 MONO_INST_NEW (cfg, dest, OP_X86_LEA); \
455 (dest)->dreg = alloc_ireg_mp ((cfg)); \
456 (dest)->sreg1 = (sr1); \
457 (dest)->sreg2 = (sr2); \
458 (dest)->inst_imm = (imm); \
459 (dest)->backend.shift_amount = (shift); \
460 MONO_ADD_INS ((cfg)->cbb, (dest)); \
464 /* Emit conversions so both operands of a binary opcode are of the same type */
466 add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **arg2_ref)
468 MonoInst *arg1 = *arg1_ref;
469 MonoInst *arg2 = *arg2_ref;
472 ((arg1->type == STACK_R4 && arg2->type == STACK_R8) ||
473 (arg1->type == STACK_R8 && arg2->type == STACK_R4))) {
476 /* Mixing r4/r8 is allowed by the spec */
477 if (arg1->type == STACK_R4) {
478 int dreg = alloc_freg (cfg);
480 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg1->dreg);
481 conv->type = STACK_R8;
485 if (arg2->type == STACK_R4) {
486 int dreg = alloc_freg (cfg);
488 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg2->dreg);
489 conv->type = STACK_R8;
495 #if SIZEOF_REGISTER == 8
496 /* FIXME: Need to add many more cases */
497 if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) {
500 int dr = alloc_preg (cfg);
501 EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg);
502 (ins)->sreg2 = widen->dreg;
507 #define ADD_BINOP(op) do { \
508 MONO_INST_NEW (cfg, ins, (op)); \
510 ins->sreg1 = sp [0]->dreg; \
511 ins->sreg2 = sp [1]->dreg; \
512 type_from_op (cfg, ins, sp [0], sp [1]); \
514 /* Have to insert a widening op */ \
515 add_widen_op (cfg, ins, &sp [0], &sp [1]); \
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_UNOP(op) do { \
522 MONO_INST_NEW (cfg, ins, (op)); \
524 ins->sreg1 = sp [0]->dreg; \
525 type_from_op (cfg, ins, sp [0], NULL); \
527 (ins)->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type); \
528 MONO_ADD_INS ((cfg)->cbb, (ins)); \
529 *sp++ = mono_decompose_opcode (cfg, ins); \
532 #define ADD_BINCOND(next_block) do { \
535 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
536 cmp->sreg1 = sp [0]->dreg; \
537 cmp->sreg2 = sp [1]->dreg; \
538 type_from_op (cfg, cmp, sp [0], sp [1]); \
540 add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
541 type_from_op (cfg, ins, sp [0], sp [1]); \
542 ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
543 GET_BBLOCK (cfg, tblock, target); \
544 link_bblock (cfg, cfg->cbb, tblock); \
545 ins->inst_true_bb = tblock; \
546 if ((next_block)) { \
547 link_bblock (cfg, cfg->cbb, (next_block)); \
548 ins->inst_false_bb = (next_block); \
549 start_new_bblock = 1; \
551 GET_BBLOCK (cfg, tblock, ip); \
552 link_bblock (cfg, cfg->cbb, tblock); \
553 ins->inst_false_bb = tblock; \
554 start_new_bblock = 2; \
556 if (sp != stack_start) { \
557 handle_stack_args (cfg, stack_start, sp - stack_start); \
558 CHECK_UNVERIFIABLE (cfg); \
560 MONO_ADD_INS (cfg->cbb, cmp); \
561 MONO_ADD_INS (cfg->cbb, ins); \
565 * link_bblock: Links two basic blocks
567 * links two basic blocks in the control flow graph, the 'from'
568 * argument is the starting block and the 'to' argument is the block
569 * the control flow ends to after 'from'.
572 link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
574 MonoBasicBlock **newa;
578 if (from->cil_code) {
580 printf ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
582 printf ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
585 printf ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
587 printf ("edge from entry to exit\n");
592 for (i = 0; i < from->out_count; ++i) {
593 if (to == from->out_bb [i]) {
599 newa = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
600 for (i = 0; i < from->out_count; ++i) {
601 newa [i] = from->out_bb [i];
609 for (i = 0; i < to->in_count; ++i) {
610 if (from == to->in_bb [i]) {
616 newa = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
617 for (i = 0; i < to->in_count; ++i) {
618 newa [i] = to->in_bb [i];
627 mono_link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
629 link_bblock (cfg, from, to);
633 * mono_find_block_region:
635 * We mark each basic block with a region ID. We use that to avoid BB
636 * optimizations when blocks are in different regions.
639 * A region token that encodes where this region is, and information
640 * about the clause owner for this block.
642 * The region encodes the try/catch/filter clause that owns this block
643 * as well as the type. -1 is a special value that represents a block
644 * that is in none of try/catch/filter.
647 mono_find_block_region (MonoCompile *cfg, int offset)
649 MonoMethodHeader *header = cfg->header;
650 MonoExceptionClause *clause;
653 for (i = 0; i < header->num_clauses; ++i) {
654 clause = &header->clauses [i];
655 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) &&
656 (offset < (clause->handler_offset)))
657 return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags;
659 if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
660 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
661 return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags;
662 else if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
663 return ((i + 1) << 8) | MONO_REGION_FAULT | clause->flags;
665 return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
668 for (i = 0; i < header->num_clauses; ++i) {
669 clause = &header->clauses [i];
671 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
672 return ((i + 1) << 8) | clause->flags;
679 mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
681 MonoMethodHeader *header = cfg->header;
682 MonoExceptionClause *clause;
686 for (i = 0; i < header->num_clauses; ++i) {
687 clause = &header->clauses [i];
688 if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
689 (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
690 if (clause->flags == type)
691 res = g_list_append (res, clause);
698 mono_create_spvar_for_region (MonoCompile *cfg, int region)
702 var = (MonoInst *)g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
706 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
707 /* prevent it from being register allocated */
708 var->flags |= MONO_INST_VOLATILE;
710 g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
714 mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
716 return (MonoInst *)g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
720 mono_create_exvar_for_offset (MonoCompile *cfg, int offset)
724 var = (MonoInst *)g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
728 var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
729 /* prevent it from being register allocated */
730 var->flags |= MONO_INST_VOLATILE;
732 g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var);
738 * Returns the type used in the eval stack when @type is loaded.
739 * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
742 type_to_eval_stack_type (MonoCompile *cfg, MonoType *type, MonoInst *inst)
746 type = mini_get_underlying_type (type);
747 inst->klass = klass = mono_class_from_mono_type (type);
749 inst->type = STACK_MP;
754 switch (type->type) {
756 inst->type = STACK_INV;
764 inst->type = STACK_I4;
769 case MONO_TYPE_FNPTR:
770 inst->type = STACK_PTR;
772 case MONO_TYPE_CLASS:
773 case MONO_TYPE_STRING:
774 case MONO_TYPE_OBJECT:
775 case MONO_TYPE_SZARRAY:
776 case MONO_TYPE_ARRAY:
777 inst->type = STACK_OBJ;
781 inst->type = STACK_I8;
784 inst->type = cfg->r4_stack_type;
787 inst->type = STACK_R8;
789 case MONO_TYPE_VALUETYPE:
790 if (type->data.klass->enumtype) {
791 type = mono_class_enum_basetype (type->data.klass);
795 inst->type = STACK_VTYPE;
798 case MONO_TYPE_TYPEDBYREF:
799 inst->klass = mono_defaults.typed_reference_class;
800 inst->type = STACK_VTYPE;
802 case MONO_TYPE_GENERICINST:
803 type = &type->data.generic_class->container_class->byval_arg;
807 g_assert (cfg->gshared);
808 if (mini_is_gsharedvt_type (type)) {
809 g_assert (cfg->gsharedvt);
810 inst->type = STACK_VTYPE;
812 type_to_eval_stack_type (cfg, mini_get_underlying_type (type), inst);
816 g_error ("unknown type 0x%02x in eval stack type", type->type);
821 * The following tables are used to quickly validate the IL code in type_from_op ().
824 bin_num_table [STACK_MAX] [STACK_MAX] = {
825 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
826 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
827 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
828 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
829 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R8},
830 {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
831 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
832 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
833 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4}
838 STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4
841 /* reduce the size of this table */
843 bin_int_table [STACK_MAX] [STACK_MAX] = {
844 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
845 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
846 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
847 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
848 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
849 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
850 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
851 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
855 bin_comp_table [STACK_MAX] [STACK_MAX] = {
856 /* Inv i L p F & O vt r4 */
858 {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
859 {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
860 {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
861 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* F, R8 */
862 {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
863 {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
864 {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
865 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* r, r4 */
868 /* reduce the size of this table */
870 shift_table [STACK_MAX] [STACK_MAX] = {
871 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
872 {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
873 {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
874 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
875 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
876 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
877 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
878 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
882 * Tables to map from the non-specific opcode to the matching
883 * type-specific opcode.
885 /* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
887 binops_op_map [STACK_MAX] = {
888 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
891 /* handles from CEE_NEG to CEE_CONV_U8 */
893 unops_op_map [STACK_MAX] = {
894 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
897 /* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
899 ovfops_op_map [STACK_MAX] = {
900 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
903 /* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
905 ovf2ops_op_map [STACK_MAX] = {
906 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
909 /* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
911 ovf3ops_op_map [STACK_MAX] = {
912 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
915 /* handles from CEE_BEQ to CEE_BLT_UN */
917 beqops_op_map [STACK_MAX] = {
918 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
921 /* handles from CEE_CEQ to CEE_CLT_UN */
923 ceqops_op_map [STACK_MAX] = {
924 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
928 * Sets ins->type (the type on the eval stack) according to the
929 * type of the opcode and the arguments to it.
930 * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
932 * FIXME: this function sets ins->type unconditionally in some cases, but
933 * it should set it to invalid for some types (a conv.x on an object)
936 type_from_op (MonoCompile *cfg, MonoInst *ins, MonoInst *src1, MonoInst *src2)
938 switch (ins->opcode) {
945 /* FIXME: check unverifiable args for STACK_MP */
946 ins->type = bin_num_table [src1->type] [src2->type];
947 ins->opcode += binops_op_map [ins->type];
954 ins->type = bin_int_table [src1->type] [src2->type];
955 ins->opcode += binops_op_map [ins->type];
960 ins->type = shift_table [src1->type] [src2->type];
961 ins->opcode += binops_op_map [ins->type];
966 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
967 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
968 ins->opcode = OP_LCOMPARE;
969 else if (src1->type == STACK_R4)
970 ins->opcode = OP_RCOMPARE;
971 else if (src1->type == STACK_R8)
972 ins->opcode = OP_FCOMPARE;
974 ins->opcode = OP_ICOMPARE;
976 case OP_ICOMPARE_IMM:
977 ins->type = bin_comp_table [src1->type] [src1->type] ? STACK_I4 : STACK_INV;
978 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
979 ins->opcode = OP_LCOMPARE_IMM;
991 ins->opcode += beqops_op_map [src1->type];
994 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
995 ins->opcode += ceqops_op_map [src1->type];
1001 ins->type = (bin_comp_table [src1->type] [src2->type] & 1) ? STACK_I4: STACK_INV;
1002 ins->opcode += ceqops_op_map [src1->type];
1006 ins->type = neg_table [src1->type];
1007 ins->opcode += unops_op_map [ins->type];
1010 if (src1->type >= STACK_I4 && src1->type <= STACK_PTR)
1011 ins->type = src1->type;
1013 ins->type = STACK_INV;
1014 ins->opcode += unops_op_map [ins->type];
1020 ins->type = STACK_I4;
1021 ins->opcode += unops_op_map [src1->type];
1024 ins->type = STACK_R8;
1025 switch (src1->type) {
1028 ins->opcode = OP_ICONV_TO_R_UN;
1031 ins->opcode = OP_LCONV_TO_R_UN;
1035 case CEE_CONV_OVF_I1:
1036 case CEE_CONV_OVF_U1:
1037 case CEE_CONV_OVF_I2:
1038 case CEE_CONV_OVF_U2:
1039 case CEE_CONV_OVF_I4:
1040 case CEE_CONV_OVF_U4:
1041 ins->type = STACK_I4;
1042 ins->opcode += ovf3ops_op_map [src1->type];
1044 case CEE_CONV_OVF_I_UN:
1045 case CEE_CONV_OVF_U_UN:
1046 ins->type = STACK_PTR;
1047 ins->opcode += ovf2ops_op_map [src1->type];
1049 case CEE_CONV_OVF_I1_UN:
1050 case CEE_CONV_OVF_I2_UN:
1051 case CEE_CONV_OVF_I4_UN:
1052 case CEE_CONV_OVF_U1_UN:
1053 case CEE_CONV_OVF_U2_UN:
1054 case CEE_CONV_OVF_U4_UN:
1055 ins->type = STACK_I4;
1056 ins->opcode += ovf2ops_op_map [src1->type];
1059 ins->type = STACK_PTR;
1060 switch (src1->type) {
1062 ins->opcode = OP_ICONV_TO_U;
1066 #if SIZEOF_VOID_P == 8
1067 ins->opcode = OP_LCONV_TO_U;
1069 ins->opcode = OP_MOVE;
1073 ins->opcode = OP_LCONV_TO_U;
1076 ins->opcode = OP_FCONV_TO_U;
1082 ins->type = STACK_I8;
1083 ins->opcode += unops_op_map [src1->type];
1085 case CEE_CONV_OVF_I8:
1086 case CEE_CONV_OVF_U8:
1087 ins->type = STACK_I8;
1088 ins->opcode += ovf3ops_op_map [src1->type];
1090 case CEE_CONV_OVF_U8_UN:
1091 case CEE_CONV_OVF_I8_UN:
1092 ins->type = STACK_I8;
1093 ins->opcode += ovf2ops_op_map [src1->type];
1096 ins->type = cfg->r4_stack_type;
1097 ins->opcode += unops_op_map [src1->type];
1100 ins->type = STACK_R8;
1101 ins->opcode += unops_op_map [src1->type];
1104 ins->type = STACK_R8;
1108 ins->type = STACK_I4;
1109 ins->opcode += ovfops_op_map [src1->type];
1112 case CEE_CONV_OVF_I:
1113 case CEE_CONV_OVF_U:
1114 ins->type = STACK_PTR;
1115 ins->opcode += ovfops_op_map [src1->type];
1118 case CEE_ADD_OVF_UN:
1120 case CEE_MUL_OVF_UN:
1122 case CEE_SUB_OVF_UN:
1123 ins->type = bin_num_table [src1->type] [src2->type];
1124 ins->opcode += ovfops_op_map [src1->type];
1125 if (ins->type == STACK_R8)
1126 ins->type = STACK_INV;
1128 case OP_LOAD_MEMBASE:
1129 ins->type = STACK_PTR;
1131 case OP_LOADI1_MEMBASE:
1132 case OP_LOADU1_MEMBASE:
1133 case OP_LOADI2_MEMBASE:
1134 case OP_LOADU2_MEMBASE:
1135 case OP_LOADI4_MEMBASE:
1136 case OP_LOADU4_MEMBASE:
1137 ins->type = STACK_PTR;
1139 case OP_LOADI8_MEMBASE:
1140 ins->type = STACK_I8;
1142 case OP_LOADR4_MEMBASE:
1143 ins->type = cfg->r4_stack_type;
1145 case OP_LOADR8_MEMBASE:
1146 ins->type = STACK_R8;
1149 g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
1153 if (ins->type == STACK_MP)
1154 ins->klass = mono_defaults.object_class;
1159 STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_R8, STACK_OBJ
1165 param_table [STACK_MAX] [STACK_MAX] = {
1170 check_values_to_signature (MonoInst *args, MonoType *this_ins, MonoMethodSignature *sig)
1175 switch (args->type) {
1185 for (i = 0; i < sig->param_count; ++i) {
1186 switch (args [i].type) {
1190 if (!sig->params [i]->byref)
1194 if (sig->params [i]->byref)
1196 switch (sig->params [i]->type) {
1197 case MONO_TYPE_CLASS:
1198 case MONO_TYPE_STRING:
1199 case MONO_TYPE_OBJECT:
1200 case MONO_TYPE_SZARRAY:
1201 case MONO_TYPE_ARRAY:
1208 if (sig->params [i]->byref)
1210 if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
1219 /*if (!param_table [args [i].type] [sig->params [i]->type])
1227 * When we need a pointer to the current domain many times in a method, we
1228 * call mono_domain_get() once and we store the result in a local variable.
1229 * This function returns the variable that represents the MonoDomain*.
1231 inline static MonoInst *
1232 mono_get_domainvar (MonoCompile *cfg)
1234 if (!cfg->domainvar)
1235 cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1236 return cfg->domainvar;
1240 * The got_var contains the address of the Global Offset Table when AOT
1244 mono_get_got_var (MonoCompile *cfg)
1246 if (!cfg->compile_aot || !cfg->backend->need_got_var)
1248 if (!cfg->got_var) {
1249 cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1251 return cfg->got_var;
1255 mono_get_vtable_var (MonoCompile *cfg)
1257 g_assert (cfg->gshared);
1259 if (!cfg->rgctx_var) {
1260 cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1261 /* force the var to be stack allocated */
1262 cfg->rgctx_var->flags |= MONO_INST_VOLATILE;
1265 return cfg->rgctx_var;
1269 type_from_stack_type (MonoInst *ins) {
1270 switch (ins->type) {
1271 case STACK_I4: return &mono_defaults.int32_class->byval_arg;
1272 case STACK_I8: return &mono_defaults.int64_class->byval_arg;
1273 case STACK_PTR: return &mono_defaults.int_class->byval_arg;
1274 case STACK_R4: return &mono_defaults.single_class->byval_arg;
1275 case STACK_R8: return &mono_defaults.double_class->byval_arg;
1277 return &ins->klass->this_arg;
1278 case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
1279 case STACK_VTYPE: return &ins->klass->byval_arg;
1281 g_error ("stack type %d to monotype not handled\n", ins->type);
1286 static G_GNUC_UNUSED int
1287 type_to_stack_type (MonoCompile *cfg, MonoType *t)
1289 t = mono_type_get_underlying_type (t);
1301 case MONO_TYPE_FNPTR:
1303 case MONO_TYPE_CLASS:
1304 case MONO_TYPE_STRING:
1305 case MONO_TYPE_OBJECT:
1306 case MONO_TYPE_SZARRAY:
1307 case MONO_TYPE_ARRAY:
1313 return cfg->r4_stack_type;
1316 case MONO_TYPE_VALUETYPE:
1317 case MONO_TYPE_TYPEDBYREF:
1319 case MONO_TYPE_GENERICINST:
1320 if (mono_type_generic_inst_is_valuetype (t))
1326 g_assert_not_reached ();
1333 array_access_to_klass (int opcode)
1337 return mono_defaults.byte_class;
1339 return mono_defaults.uint16_class;
1342 return mono_defaults.int_class;
1345 return mono_defaults.sbyte_class;
1348 return mono_defaults.int16_class;
1351 return mono_defaults.int32_class;
1353 return mono_defaults.uint32_class;
1356 return mono_defaults.int64_class;
1359 return mono_defaults.single_class;
1362 return mono_defaults.double_class;
1363 case CEE_LDELEM_REF:
1364 case CEE_STELEM_REF:
1365 return mono_defaults.object_class;
1367 g_assert_not_reached ();
1373 * We try to share variables when possible
1376 mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
1381 /* inlining can result in deeper stacks */
1382 if (slot >= cfg->header->max_stack)
1383 return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1385 pos = ins->type - 1 + slot * STACK_MAX;
1387 switch (ins->type) {
1394 if ((vnum = cfg->intvars [pos]))
1395 return cfg->varinfo [vnum];
1396 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1397 cfg->intvars [pos] = res->inst_c0;
1400 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1406 mono_save_token_info (MonoCompile *cfg, MonoImage *image, guint32 token, gpointer key)
1409 * Don't use this if a generic_context is set, since that means AOT can't
1410 * look up the method using just the image+token.
1411 * table == 0 means this is a reference made from a wrapper.
1413 if (cfg->compile_aot && !cfg->generic_context && (mono_metadata_token_table (token) > 0)) {
1414 MonoJumpInfoToken *jump_info_token = (MonoJumpInfoToken *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoToken));
1415 jump_info_token->image = image;
1416 jump_info_token->token = token;
1417 g_hash_table_insert (cfg->token_info_hash, key, jump_info_token);
1422 * This function is called to handle items that are left on the evaluation stack
1423 * at basic block boundaries. What happens is that we save the values to local variables
1424 * and we reload them later when first entering the target basic block (with the
1425 * handle_loaded_temps () function).
1426 * A single joint point will use the same variables (stored in the array bb->out_stack or
1427 * bb->in_stack, if the basic block is before or after the joint point).
1429 * This function needs to be called _before_ emitting the last instruction of
1430 * the bb (i.e. before emitting a branch).
1431 * If the stack merge fails at a join point, cfg->unverifiable is set.
1434 handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
1437 MonoBasicBlock *bb = cfg->cbb;
1438 MonoBasicBlock *outb;
1439 MonoInst *inst, **locals;
1444 if (cfg->verbose_level > 3)
1445 printf ("%d item(s) on exit from B%d\n", count, bb->block_num);
1446 if (!bb->out_scount) {
1447 bb->out_scount = count;
1448 //printf ("bblock %d has out:", bb->block_num);
1450 for (i = 0; i < bb->out_count; ++i) {
1451 outb = bb->out_bb [i];
1452 /* exception handlers are linked, but they should not be considered for stack args */
1453 if (outb->flags & BB_EXCEPTION_HANDLER)
1455 //printf (" %d", outb->block_num);
1456 if (outb->in_stack) {
1458 bb->out_stack = outb->in_stack;
1464 bb->out_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
1465 for (i = 0; i < count; ++i) {
1467 * try to reuse temps already allocated for this purpouse, if they occupy the same
1468 * stack slot and if they are of the same type.
1469 * This won't cause conflicts since if 'local' is used to
1470 * store one of the values in the in_stack of a bblock, then
1471 * the same variable will be used for the same outgoing stack
1473 * This doesn't work when inlining methods, since the bblocks
1474 * in the inlined methods do not inherit their in_stack from
1475 * the bblock they are inlined to. See bug #58863 for an
1478 if (cfg->inlined_method)
1479 bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
1481 bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
1486 for (i = 0; i < bb->out_count; ++i) {
1487 outb = bb->out_bb [i];
1488 /* exception handlers are linked, but they should not be considered for stack args */
1489 if (outb->flags & BB_EXCEPTION_HANDLER)
1491 if (outb->in_scount) {
1492 if (outb->in_scount != bb->out_scount) {
1493 cfg->unverifiable = TRUE;
1496 continue; /* check they are the same locals */
1498 outb->in_scount = count;
1499 outb->in_stack = bb->out_stack;
1502 locals = bb->out_stack;
1504 for (i = 0; i < count; ++i) {
1505 EMIT_NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
1506 inst->cil_code = sp [i]->cil_code;
1507 sp [i] = locals [i];
1508 if (cfg->verbose_level > 3)
1509 printf ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0);
1513 * It is possible that the out bblocks already have in_stack assigned, and
1514 * the in_stacks differ. In this case, we will store to all the different
1521 /* Find a bblock which has a different in_stack */
1523 while (bindex < bb->out_count) {
1524 outb = bb->out_bb [bindex];
1525 /* exception handlers are linked, but they should not be considered for stack args */
1526 if (outb->flags & BB_EXCEPTION_HANDLER) {
1530 if (outb->in_stack != locals) {
1531 for (i = 0; i < count; ++i) {
1532 EMIT_NEW_TEMPSTORE (cfg, inst, outb->in_stack [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)outb->in_stack [i]->inst_c0);
1538 locals = outb->in_stack;
1548 emit_runtime_constant (MonoCompile *cfg, MonoJumpInfoType patch_type, gpointer data)
1552 if (cfg->compile_aot) {
1553 EMIT_NEW_AOTCONST (cfg, ins, patch_type, data);
1559 ji.type = patch_type;
1560 ji.data.target = data;
1561 target = mono_resolve_patch_target (NULL, cfg->domain, NULL, &ji, FALSE, &error);
1562 mono_error_assert_ok (&error);
1564 EMIT_NEW_PCONST (cfg, ins, target);
1570 mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
1572 int ibitmap_reg = alloc_preg (cfg);
1573 #ifdef COMPRESSED_INTERFACE_BITMAP
1575 MonoInst *res, *ins;
1576 NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
1577 MONO_ADD_INS (cfg->cbb, ins);
1579 args [1] = emit_runtime_constant (cfg, MONO_PATCH_INFO_IID, klass);
1580 res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
1581 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
1583 int ibitmap_byte_reg = alloc_preg (cfg);
1585 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
1587 if (cfg->compile_aot) {
1588 int iid_reg = alloc_preg (cfg);
1589 int shifted_iid_reg = alloc_preg (cfg);
1590 int ibitmap_byte_address_reg = alloc_preg (cfg);
1591 int masked_iid_reg = alloc_preg (cfg);
1592 int iid_one_bit_reg = alloc_preg (cfg);
1593 int iid_bit_reg = alloc_preg (cfg);
1594 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1595 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3);
1596 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
1597 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
1598 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7);
1599 MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
1600 MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
1601 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
1603 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
1604 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
1610 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
1611 * stored in "klass_reg" implements the interface "klass".
1614 mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
1616 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, interface_bitmap), klass);
1620 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
1621 * stored in "vtable_reg" implements the interface "klass".
1624 mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
1626 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
1630 * Emit code which checks whenever the interface id of @klass is smaller than
1631 * than the value given by max_iid_reg.
1634 mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass,
1635 MonoBasicBlock *false_target)
1637 if (cfg->compile_aot) {
1638 int iid_reg = alloc_preg (cfg);
1639 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1640 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg);
1643 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
1645 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1647 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1650 /* Same as above, but obtains max_iid from a vtable */
1652 mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass,
1653 MonoBasicBlock *false_target)
1655 int max_iid_reg = alloc_preg (cfg);
1657 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
1658 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1661 /* Same as above, but obtains max_iid from a klass */
1663 mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass,
1664 MonoBasicBlock *false_target)
1666 int max_iid_reg = alloc_preg (cfg);
1668 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, max_interface_id));
1669 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1673 mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1675 int idepth_reg = alloc_preg (cfg);
1676 int stypes_reg = alloc_preg (cfg);
1677 int stype = alloc_preg (cfg);
1679 mono_class_setup_supertypes (klass);
1681 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1682 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1683 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1684 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1686 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1687 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1689 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
1690 } else if (cfg->compile_aot) {
1691 int const_reg = alloc_preg (cfg);
1692 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1693 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
1695 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass);
1697 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
1701 mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1703 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
1707 mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1709 int intf_reg = alloc_preg (cfg);
1711 mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target);
1712 mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass);
1713 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0);
1715 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1717 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1721 * Variant of the above that takes a register to the class, not the vtable.
1724 mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1726 int intf_bit_reg = alloc_preg (cfg);
1728 mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target);
1729 mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass);
1730 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0);
1732 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1734 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1738 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
1741 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
1743 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
1744 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, ins->dreg);
1746 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1750 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
1752 mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
1756 mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
1758 if (cfg->compile_aot) {
1759 int const_reg = alloc_preg (cfg);
1760 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1761 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1763 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1765 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
1769 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
1772 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
1775 int rank_reg = alloc_preg (cfg);
1776 int eclass_reg = alloc_preg (cfg);
1778 g_assert (!klass_inst);
1779 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, rank));
1780 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
1781 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1782 // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
1783 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
1784 if (klass->cast_class == mono_defaults.object_class) {
1785 int parent_reg = alloc_preg (cfg);
1786 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
1787 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, object_is_null);
1788 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1789 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
1790 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, object_is_null);
1791 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1792 } else if (klass->cast_class == mono_defaults.enum_class) {
1793 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1794 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
1795 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, NULL, NULL);
1797 // Pass -1 as obj_reg to skip the check below for arrays of arrays
1798 mini_emit_castclass (cfg, -1, eclass_reg, klass->cast_class, object_is_null);
1801 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) {
1802 /* Check that the object is a vector too */
1803 int bounds_reg = alloc_preg (cfg);
1804 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
1805 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
1806 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1809 int idepth_reg = alloc_preg (cfg);
1810 int stypes_reg = alloc_preg (cfg);
1811 int stype = alloc_preg (cfg);
1813 mono_class_setup_supertypes (klass);
1815 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1816 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1817 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1818 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1820 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1821 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1822 mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
1827 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
1829 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
1833 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
1837 g_assert (val == 0);
1842 if ((size <= SIZEOF_REGISTER) && (size <= align)) {
1845 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
1848 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
1851 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
1853 #if SIZEOF_REGISTER == 8
1855 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
1861 val_reg = alloc_preg (cfg);
1863 if (SIZEOF_REGISTER == 8)
1864 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
1866 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
1869 /* This could be optimized further if neccesary */
1871 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1878 if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
1880 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1885 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
1892 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1897 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
1902 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1909 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
1916 /*FIXME arbitrary hack to avoid unbound code expansion.*/
1917 g_assert (size < 10000);
1920 /* This could be optimized further if neccesary */
1922 cur_reg = alloc_preg (cfg);
1923 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1924 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1931 if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
1933 cur_reg = alloc_preg (cfg);
1934 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
1935 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
1943 cur_reg = alloc_preg (cfg);
1944 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
1945 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
1951 cur_reg = alloc_preg (cfg);
1952 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
1953 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
1959 cur_reg = alloc_preg (cfg);
1960 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1961 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1969 emit_tls_set (MonoCompile *cfg, int sreg1, MonoTlsKey tls_key)
1973 if (cfg->compile_aot) {
1974 EMIT_NEW_TLS_OFFSETCONST (cfg, c, tls_key);
1975 MONO_INST_NEW (cfg, ins, OP_TLS_SET_REG);
1977 ins->sreg2 = c->dreg;
1978 MONO_ADD_INS (cfg->cbb, ins);
1980 MONO_INST_NEW (cfg, ins, OP_TLS_SET);
1982 ins->inst_offset = mini_get_tls_offset (tls_key);
1983 MONO_ADD_INS (cfg->cbb, ins);
1990 * Emit IR to push the current LMF onto the LMF stack.
1993 emit_push_lmf (MonoCompile *cfg)
1996 * Emit IR to push the LMF:
1997 * lmf_addr = <lmf_addr from tls>
1998 * lmf->lmf_addr = lmf_addr
1999 * lmf->prev_lmf = *lmf_addr
2002 int lmf_reg, prev_lmf_reg;
2003 MonoInst *ins, *lmf_ins;
2008 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2009 /* Load current lmf */
2010 lmf_ins = mono_get_lmf_intrinsic (cfg);
2012 MONO_ADD_INS (cfg->cbb, lmf_ins);
2013 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2014 lmf_reg = ins->dreg;
2015 /* Save previous_lmf */
2016 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), lmf_ins->dreg);
2018 emit_tls_set (cfg, lmf_reg, TLS_KEY_LMF);
2021 * Store lmf_addr in a variable, so it can be allocated to a global register.
2023 if (!cfg->lmf_addr_var)
2024 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2027 ins = mono_get_jit_tls_intrinsic (cfg);
2029 int jit_tls_dreg = ins->dreg;
2031 MONO_ADD_INS (cfg->cbb, ins);
2032 lmf_reg = alloc_preg (cfg);
2033 EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2035 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2038 lmf_ins = mono_get_lmf_addr_intrinsic (cfg);
2040 MONO_ADD_INS (cfg->cbb, lmf_ins);
2043 MonoInst *args [16], *jit_tls_ins, *ins;
2045 /* Inline mono_get_lmf_addr () */
2046 /* jit_tls = pthread_getspecific (mono_jit_tls_id); lmf_addr = &jit_tls->lmf; */
2048 /* Load mono_jit_tls_id */
2049 if (cfg->compile_aot)
2050 EMIT_NEW_AOTCONST (cfg, args [0], MONO_PATCH_INFO_JIT_TLS_ID, NULL);
2052 EMIT_NEW_ICONST (cfg, args [0], mono_jit_tls_id);
2053 /* call pthread_getspecific () */
2054 jit_tls_ins = mono_emit_jit_icall (cfg, pthread_getspecific, args);
2055 /* lmf_addr = &jit_tls->lmf */
2056 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, cfg->lmf_addr_var->dreg, jit_tls_ins->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2059 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2063 lmf_ins->dreg = cfg->lmf_addr_var->dreg;
2065 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2066 lmf_reg = ins->dreg;
2068 prev_lmf_reg = alloc_preg (cfg);
2069 /* Save previous_lmf */
2070 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0);
2071 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg);
2073 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg);
2080 * Emit IR to pop the current LMF from the LMF stack.
2083 emit_pop_lmf (MonoCompile *cfg)
2085 int lmf_reg, lmf_addr_reg, prev_lmf_reg;
2091 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2092 lmf_reg = ins->dreg;
2094 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2095 /* Load previous_lmf */
2096 prev_lmf_reg = alloc_preg (cfg);
2097 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2099 emit_tls_set (cfg, prev_lmf_reg, TLS_KEY_LMF);
2102 * Emit IR to pop the LMF:
2103 * *(lmf->lmf_addr) = lmf->prev_lmf
2105 /* This could be called before emit_push_lmf () */
2106 if (!cfg->lmf_addr_var)
2107 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2108 lmf_addr_reg = cfg->lmf_addr_var->dreg;
2110 prev_lmf_reg = alloc_preg (cfg);
2111 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2112 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
2117 emit_instrumentation_call (MonoCompile *cfg, void *func)
2119 MonoInst *iargs [1];
2122 * Avoid instrumenting inlined methods since it can
2123 * distort profiling results.
2125 if (cfg->method != cfg->current_method)
2128 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
2129 EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
2130 mono_emit_jit_icall (cfg, func, iargs);
2135 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt)
2138 type = mini_get_underlying_type (type);
2139 switch (type->type) {
2140 case MONO_TYPE_VOID:
2141 return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALL_MEMBASE: OP_VOIDCALL;
2148 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2152 case MONO_TYPE_FNPTR:
2153 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2154 case MONO_TYPE_CLASS:
2155 case MONO_TYPE_STRING:
2156 case MONO_TYPE_OBJECT:
2157 case MONO_TYPE_SZARRAY:
2158 case MONO_TYPE_ARRAY:
2159 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2162 return calli? OP_LCALL_REG: virt? OP_LCALL_MEMBASE: OP_LCALL;
2165 return calli? OP_RCALL_REG: virt? OP_RCALL_MEMBASE: OP_RCALL;
2167 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2169 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2170 case MONO_TYPE_VALUETYPE:
2171 if (type->data.klass->enumtype) {
2172 type = mono_class_enum_basetype (type->data.klass);
2175 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2176 case MONO_TYPE_TYPEDBYREF:
2177 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2178 case MONO_TYPE_GENERICINST:
2179 type = &type->data.generic_class->container_class->byval_arg;
2182 case MONO_TYPE_MVAR:
2184 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2186 g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type);
2191 //XXX this ignores if t is byref
2192 #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)))))
2195 * target_type_is_incompatible:
2196 * @cfg: MonoCompile context
2198 * Check that the item @arg on the evaluation stack can be stored
2199 * in the target type (can be a local, or field, etc).
2200 * The cfg arg can be used to check if we need verification or just
2203 * Returns: non-0 value if arg can't be stored on a target.
2206 target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
2208 MonoType *simple_type;
2211 if (target->byref) {
2212 /* FIXME: check that the pointed to types match */
2213 if (arg->type == STACK_MP) {
2214 if (cfg->verbose_level) printf ("ok\n");
2215 /* This is needed to handle gshared types + ldaddr. We lower the types so we can handle enums and other typedef-like types. */
2216 MonoClass *target_class_lowered = mono_class_from_mono_type (mini_get_underlying_type (&mono_class_from_mono_type (target)->byval_arg));
2217 MonoClass *source_class_lowered = mono_class_from_mono_type (mini_get_underlying_type (&arg->klass->byval_arg));
2219 /* if the target is native int& or same type */
2220 if (target->type == MONO_TYPE_I || target_class_lowered == source_class_lowered)
2223 /* Both are primitive type byrefs and the source points to a larger type that the destination */
2224 if (MONO_TYPE_IS_PRIMITIVE_SCALAR (&target_class_lowered->byval_arg) && MONO_TYPE_IS_PRIMITIVE_SCALAR (&source_class_lowered->byval_arg) &&
2225 mono_class_instance_size (target_class_lowered) <= mono_class_instance_size (source_class_lowered))
2229 if (arg->type == STACK_PTR)
2234 simple_type = mini_get_underlying_type (target);
2235 switch (simple_type->type) {
2236 case MONO_TYPE_VOID:
2244 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
2248 /* STACK_MP is needed when setting pinned locals */
2249 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2254 case MONO_TYPE_FNPTR:
2256 * Some opcodes like ldloca returns 'transient pointers' which can be stored in
2257 * in native int. (#688008).
2259 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2262 case MONO_TYPE_CLASS:
2263 case MONO_TYPE_STRING:
2264 case MONO_TYPE_OBJECT:
2265 case MONO_TYPE_SZARRAY:
2266 case MONO_TYPE_ARRAY:
2267 if (arg->type != STACK_OBJ)
2269 /* FIXME: check type compatibility */
2273 if (arg->type != STACK_I8)
2277 if (arg->type != cfg->r4_stack_type)
2281 if (arg->type != STACK_R8)
2284 case MONO_TYPE_VALUETYPE:
2285 if (arg->type != STACK_VTYPE)
2287 klass = mono_class_from_mono_type (simple_type);
2288 if (klass != arg->klass)
2291 case MONO_TYPE_TYPEDBYREF:
2292 if (arg->type != STACK_VTYPE)
2294 klass = mono_class_from_mono_type (simple_type);
2295 if (klass != arg->klass)
2298 case MONO_TYPE_GENERICINST:
2299 if (mono_type_generic_inst_is_valuetype (simple_type)) {
2300 MonoClass *target_class;
2301 if (arg->type != STACK_VTYPE)
2303 klass = mono_class_from_mono_type (simple_type);
2304 target_class = mono_class_from_mono_type (target);
2305 /* The second cases is needed when doing partial sharing */
2306 if (klass != arg->klass && target_class != arg->klass && target_class != mono_class_from_mono_type (mini_get_underlying_type (&arg->klass->byval_arg)))
2310 if (arg->type != STACK_OBJ)
2312 /* FIXME: check type compatibility */
2316 case MONO_TYPE_MVAR:
2317 g_assert (cfg->gshared);
2318 if (mini_type_var_is_vt (simple_type)) {
2319 if (arg->type != STACK_VTYPE)
2322 if (arg->type != STACK_OBJ)
2327 g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
2333 * Prepare arguments for passing to a function call.
2334 * Return a non-zero value if the arguments can't be passed to the given
2336 * The type checks are not yet complete and some conversions may need
2337 * casts on 32 or 64 bit architectures.
2339 * FIXME: implement this using target_type_is_incompatible ()
2342 check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
2344 MonoType *simple_type;
2348 if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR)
2352 for (i = 0; i < sig->param_count; ++i) {
2353 if (sig->params [i]->byref) {
2354 if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR)
2358 simple_type = mini_get_underlying_type (sig->params [i]);
2360 switch (simple_type->type) {
2361 case MONO_TYPE_VOID:
2370 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR)
2376 case MONO_TYPE_FNPTR:
2377 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ)
2380 case MONO_TYPE_CLASS:
2381 case MONO_TYPE_STRING:
2382 case MONO_TYPE_OBJECT:
2383 case MONO_TYPE_SZARRAY:
2384 case MONO_TYPE_ARRAY:
2385 if (args [i]->type != STACK_OBJ)
2390 if (args [i]->type != STACK_I8)
2394 if (args [i]->type != cfg->r4_stack_type)
2398 if (args [i]->type != STACK_R8)
2401 case MONO_TYPE_VALUETYPE:
2402 if (simple_type->data.klass->enumtype) {
2403 simple_type = mono_class_enum_basetype (simple_type->data.klass);
2406 if (args [i]->type != STACK_VTYPE)
2409 case MONO_TYPE_TYPEDBYREF:
2410 if (args [i]->type != STACK_VTYPE)
2413 case MONO_TYPE_GENERICINST:
2414 simple_type = &simple_type->data.generic_class->container_class->byval_arg;
2417 case MONO_TYPE_MVAR:
2419 if (args [i]->type != STACK_VTYPE)
2423 g_error ("unknown type 0x%02x in check_call_signature",
2431 callvirt_to_call (int opcode)
2434 case OP_CALL_MEMBASE:
2436 case OP_VOIDCALL_MEMBASE:
2438 case OP_FCALL_MEMBASE:
2440 case OP_RCALL_MEMBASE:
2442 case OP_VCALL_MEMBASE:
2444 case OP_LCALL_MEMBASE:
2447 g_assert_not_reached ();
2454 callvirt_to_call_reg (int opcode)
2457 case OP_CALL_MEMBASE:
2459 case OP_VOIDCALL_MEMBASE:
2460 return OP_VOIDCALL_REG;
2461 case OP_FCALL_MEMBASE:
2462 return OP_FCALL_REG;
2463 case OP_RCALL_MEMBASE:
2464 return OP_RCALL_REG;
2465 case OP_VCALL_MEMBASE:
2466 return OP_VCALL_REG;
2467 case OP_LCALL_MEMBASE:
2468 return OP_LCALL_REG;
2470 g_assert_not_reached ();
2476 /* Either METHOD or IMT_ARG needs to be set */
2478 emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoMethod *method, MonoInst *imt_arg)
2482 if (COMPILE_LLVM (cfg)) {
2484 method_reg = alloc_preg (cfg);
2485 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2487 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHODCONST, method);
2488 method_reg = ins->dreg;
2492 call->imt_arg_reg = method_reg;
2494 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2499 method_reg = alloc_preg (cfg);
2500 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2502 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHODCONST, method);
2503 method_reg = ins->dreg;
2506 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2509 static MonoJumpInfo *
2510 mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpointer target)
2512 MonoJumpInfo *ji = (MonoJumpInfo *)mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
2516 ji->data.target = target;
2522 mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass)
2525 return mono_class_check_context_used (klass);
2531 mini_method_check_context_used (MonoCompile *cfg, MonoMethod *method)
2534 return mono_method_check_context_used (method);
2540 * check_method_sharing:
2542 * Check whenever the vtable or an mrgctx needs to be passed when calling CMETHOD.
2545 check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_vtable, gboolean *out_pass_mrgctx)
2547 gboolean pass_vtable = FALSE;
2548 gboolean pass_mrgctx = FALSE;
2550 if (((cmethod->flags & METHOD_ATTRIBUTE_STATIC) || cmethod->klass->valuetype) &&
2551 (cmethod->klass->generic_class || cmethod->klass->generic_container)) {
2552 gboolean sharable = FALSE;
2554 if (mono_method_is_generic_sharable_full (cmethod, TRUE, TRUE, TRUE))
2558 * Pass vtable iff target method might
2559 * be shared, which means that sharing
2560 * is enabled for its class and its
2561 * context is sharable (and it's not a
2564 if (sharable && !(mini_method_get_context (cmethod) && mini_method_get_context (cmethod)->method_inst))
2568 if (mini_method_get_context (cmethod) &&
2569 mini_method_get_context (cmethod)->method_inst) {
2570 g_assert (!pass_vtable);
2572 if (mono_method_is_generic_sharable_full (cmethod, TRUE, TRUE, TRUE)) {
2575 if (cfg->gsharedvt && mini_is_gsharedvt_signature (mono_method_signature (cmethod)))
2580 if (out_pass_vtable)
2581 *out_pass_vtable = pass_vtable;
2582 if (out_pass_mrgctx)
2583 *out_pass_mrgctx = pass_mrgctx;
2586 inline static MonoCallInst *
2587 mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
2588 MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline)
2592 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2600 emit_instrumentation_call (cfg, mono_profiler_method_leave);
2602 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
2604 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (cfg, sig->ret, calli, virtual_));
2607 call->signature = sig;
2608 call->rgctx_reg = rgctx;
2609 sig_ret = mini_get_underlying_type (sig->ret);
2611 type_to_eval_stack_type ((cfg), sig_ret, &call->inst);
2614 if (mini_type_is_vtype (sig_ret)) {
2615 call->vret_var = cfg->vret_addr;
2616 //g_assert_not_reached ();
2618 } else if (mini_type_is_vtype (sig_ret)) {
2619 MonoInst *temp = mono_compile_create_var (cfg, sig_ret, OP_LOCAL);
2622 temp->backend.is_pinvoke = sig->pinvoke;
2625 * We use a new opcode OP_OUTARG_VTRETADDR instead of LDADDR for emitting the
2626 * address of return value to increase optimization opportunities.
2627 * Before vtype decomposition, the dreg of the call ins itself represents the
2628 * fact the call modifies the return value. After decomposition, the call will
2629 * be transformed into one of the OP_VOIDCALL opcodes, and the VTRETADDR opcode
2630 * will be transformed into an LDADDR.
2632 MONO_INST_NEW (cfg, loada, OP_OUTARG_VTRETADDR);
2633 loada->dreg = alloc_preg (cfg);
2634 loada->inst_p0 = temp;
2635 /* We reference the call too since call->dreg could change during optimization */
2636 loada->inst_p1 = call;
2637 MONO_ADD_INS (cfg->cbb, loada);
2639 call->inst.dreg = temp->dreg;
2641 call->vret_var = loada;
2642 } else if (!MONO_TYPE_IS_VOID (sig_ret))
2643 call->inst.dreg = alloc_dreg (cfg, (MonoStackType)call->inst.type);
2645 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2646 if (COMPILE_SOFT_FLOAT (cfg)) {
2648 * If the call has a float argument, we would need to do an r8->r4 conversion using
2649 * an icall, but that cannot be done during the call sequence since it would clobber
2650 * the call registers + the stack. So we do it before emitting the call.
2652 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2654 MonoInst *in = call->args [i];
2656 if (i >= sig->hasthis)
2657 t = sig->params [i - sig->hasthis];
2659 t = &mono_defaults.int_class->byval_arg;
2660 t = mono_type_get_underlying_type (t);
2662 if (!t->byref && t->type == MONO_TYPE_R4) {
2663 MonoInst *iargs [1];
2667 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
2669 /* The result will be in an int vreg */
2670 call->args [i] = conv;
2676 call->need_unbox_trampoline = unbox_trampoline;
2679 if (COMPILE_LLVM (cfg))
2680 mono_llvm_emit_call (cfg, call);
2682 mono_arch_emit_call (cfg, call);
2684 mono_arch_emit_call (cfg, call);
2687 cfg->param_area = MAX (cfg->param_area, call->stack_usage);
2688 cfg->flags |= MONO_CFG_HAS_CALLS;
2694 set_rgctx_arg (MonoCompile *cfg, MonoCallInst *call, int rgctx_reg, MonoInst *rgctx_arg)
2696 mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
2697 cfg->uses_rgctx_reg = TRUE;
2698 call->rgctx_reg = TRUE;
2700 call->rgctx_arg_reg = rgctx_reg;
2704 inline static MonoInst*
2705 mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg)
2710 gboolean check_sp = FALSE;
2712 if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
2713 WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method);
2715 if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE)
2720 rgctx_reg = mono_alloc_preg (cfg);
2721 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2725 if (!cfg->stack_inbalance_var)
2726 cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2728 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2729 ins->dreg = cfg->stack_inbalance_var->dreg;
2730 MONO_ADD_INS (cfg->cbb, ins);
2733 call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
2735 call->inst.sreg1 = addr->dreg;
2738 emit_imt_argument (cfg, call, NULL, imt_arg);
2740 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2745 sp_reg = mono_alloc_preg (cfg);
2747 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2749 MONO_ADD_INS (cfg->cbb, ins);
2751 /* Restore the stack so we don't crash when throwing the exception */
2752 MONO_INST_NEW (cfg, ins, OP_SET_SP);
2753 ins->sreg1 = cfg->stack_inbalance_var->dreg;
2754 MONO_ADD_INS (cfg->cbb, ins);
2756 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg);
2757 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException");
2761 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2763 return (MonoInst*)call;
2767 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2770 emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type);
2772 emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2775 mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, gboolean tail,
2776 MonoInst **args, MonoInst *this_ins, MonoInst *imt_arg, MonoInst *rgctx_arg)
2778 #ifndef DISABLE_REMOTING
2779 gboolean might_be_remote = FALSE;
2781 gboolean virtual_ = this_ins != NULL;
2782 gboolean enable_for_aot = TRUE;
2785 MonoInst *call_target = NULL;
2787 gboolean need_unbox_trampoline;
2790 sig = mono_method_signature (method);
2792 if (cfg->llvm_only && (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE))
2793 g_assert_not_reached ();
2796 rgctx_reg = mono_alloc_preg (cfg);
2797 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2800 if (method->string_ctor) {
2801 /* Create the real signature */
2802 /* FIXME: Cache these */
2803 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig);
2804 ctor_sig->ret = &mono_defaults.string_class->byval_arg;
2809 context_used = mini_method_check_context_used (cfg, method);
2811 #ifndef DISABLE_REMOTING
2812 might_be_remote = this_ins && sig->hasthis &&
2813 (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) &&
2814 !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && (!MONO_CHECK_THIS (this_ins) || context_used);
2816 if (might_be_remote && context_used) {
2819 g_assert (cfg->gshared);
2821 addr = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK);
2823 return mono_emit_calli (cfg, sig, args, addr, NULL, NULL);
2827 if (cfg->llvm_only && !call_target && virtual_ && (method->flags & METHOD_ATTRIBUTE_VIRTUAL))
2828 return emit_llvmonly_virtual_call (cfg, method, sig, 0, args);
2830 need_unbox_trampoline = method->klass == mono_defaults.object_class || (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
2832 call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
2834 #ifndef DISABLE_REMOTING
2835 if (might_be_remote)
2836 call->method = mono_marshal_get_remoting_invoke_with_check (method);
2839 call->method = method;
2840 call->inst.flags |= MONO_INST_HAS_METHOD;
2841 call->inst.inst_left = this_ins;
2842 call->tail_call = tail;
2845 int vtable_reg, slot_reg, this_reg;
2848 this_reg = this_ins->dreg;
2850 if (!cfg->llvm_only && (method->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (method->name, "Invoke")) {
2851 MonoInst *dummy_use;
2853 MONO_EMIT_NULL_CHECK (cfg, this_reg);
2855 /* Make a call to delegate->invoke_impl */
2856 call->inst.inst_basereg = this_reg;
2857 call->inst.inst_offset = MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl);
2858 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2860 /* We must emit a dummy use here because the delegate trampoline will
2861 replace the 'this' argument with the delegate target making this activation
2862 no longer a root for the delegate.
2863 This is an issue for delegates that target collectible code such as dynamic
2864 methods of GC'able assemblies.
2866 For a test case look into #667921.
2868 FIXME: a dummy use is not the best way to do it as the local register allocator
2869 will put it on a caller save register and spil it around the call.
2870 Ideally, we would either put it on a callee save register or only do the store part.
2872 EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [0]);
2874 return (MonoInst*)call;
2877 if ((!cfg->compile_aot || enable_for_aot) &&
2878 (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2879 (MONO_METHOD_IS_FINAL (method) &&
2880 method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2881 !(mono_class_is_marshalbyref (method->klass) && context_used)) {
2883 * the method is not virtual, we just need to ensure this is not null
2884 * and then we can call the method directly.
2886 #ifndef DISABLE_REMOTING
2887 if (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) {
2889 * The check above ensures method is not gshared, this is needed since
2890 * gshared methods can't have wrappers.
2892 method = call->method = mono_marshal_get_remoting_invoke_with_check (method);
2896 if (!method->string_ctor)
2897 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2899 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2900 } else if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && MONO_METHOD_IS_FINAL (method)) {
2902 * the method is virtual, but we can statically dispatch since either
2903 * it's class or the method itself are sealed.
2904 * But first we need to ensure it's not a null reference.
2906 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2908 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2909 } else if (call_target) {
2910 vtable_reg = alloc_preg (cfg);
2911 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2913 call->inst.opcode = callvirt_to_call_reg (call->inst.opcode);
2914 call->inst.sreg1 = call_target->dreg;
2915 call->inst.flags &= !MONO_INST_HAS_METHOD;
2917 vtable_reg = alloc_preg (cfg);
2918 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2919 if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2920 guint32 imt_slot = mono_method_get_imt_slot (method);
2921 emit_imt_argument (cfg, call, call->method, imt_arg);
2922 slot_reg = vtable_reg;
2923 offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
2925 slot_reg = vtable_reg;
2926 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) +
2927 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
2929 g_assert (mono_method_signature (method)->generic_param_count);
2930 emit_imt_argument (cfg, call, call->method, imt_arg);
2934 call->inst.sreg1 = slot_reg;
2935 call->inst.inst_offset = offset;
2936 call->is_virtual = TRUE;
2940 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2943 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2945 return (MonoInst*)call;
2949 mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this_ins)
2951 return mono_emit_method_call_full (cfg, method, mono_method_signature (method), FALSE, args, this_ins, NULL, NULL);
2955 mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig,
2962 call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
2965 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2967 return (MonoInst*)call;
2971 mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args)
2973 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
2977 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
2981 * mono_emit_abs_call:
2983 * Emit a call to the runtime function described by PATCH_TYPE and DATA.
2985 inline static MonoInst*
2986 mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer data,
2987 MonoMethodSignature *sig, MonoInst **args)
2989 MonoJumpInfo *ji = mono_patch_info_new (cfg->mempool, 0, patch_type, data);
2993 * We pass ji as the call address, the PATCH_INFO_ABS resolving code will
2996 if (cfg->abs_patches == NULL)
2997 cfg->abs_patches = g_hash_table_new (NULL, NULL);
2998 g_hash_table_insert (cfg->abs_patches, ji, ji);
2999 ins = mono_emit_native_call (cfg, ji, sig, args);
3000 ((MonoCallInst*)ins)->fptr_is_patch = TRUE;
3004 static MonoMethodSignature*
3005 sig_to_rgctx_sig (MonoMethodSignature *sig)
3007 // FIXME: memory allocation
3008 MonoMethodSignature *res;
3011 res = (MonoMethodSignature *)g_malloc (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count + 1) * sizeof (MonoType*));
3012 memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE);
3013 res->param_count = sig->param_count + 1;
3014 for (i = 0; i < sig->param_count; ++i)
3015 res->params [i] = sig->params [i];
3016 res->params [sig->param_count] = &mono_defaults.int_class->this_arg;
3020 /* Make an indirect call to FSIG passing an additional argument */
3022 emit_extra_arg_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **orig_args, int arg_reg, MonoInst *call_target)
3024 MonoMethodSignature *csig;
3025 MonoInst *args_buf [16];
3027 int i, pindex, tmp_reg;
3029 /* Make a call with an rgctx/extra arg */
3030 if (fsig->param_count + 2 < 16)
3033 args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (fsig->param_count + 2));
3036 args [pindex ++] = orig_args [0];
3037 for (i = 0; i < fsig->param_count; ++i)
3038 args [pindex ++] = orig_args [fsig->hasthis + i];
3039 tmp_reg = alloc_preg (cfg);
3040 EMIT_NEW_UNALU (cfg, args [pindex], OP_MOVE, tmp_reg, arg_reg);
3041 csig = sig_to_rgctx_sig (fsig);
3042 return mono_emit_calli (cfg, csig, args, call_target, NULL, NULL);
3045 /* Emit an indirect call to the function descriptor ADDR */
3047 emit_llvmonly_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, MonoInst *addr)
3049 int addr_reg, arg_reg;
3050 MonoInst *call_target;
3052 g_assert (cfg->llvm_only);
3055 * addr points to a <addr, arg> pair, load both of them, and
3056 * make a call to addr, passing arg as an extra arg.
3058 addr_reg = alloc_preg (cfg);
3059 EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, addr->dreg, 0);
3060 arg_reg = alloc_preg (cfg);
3061 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, addr->dreg, sizeof (gpointer));
3063 return emit_extra_arg_calli (cfg, fsig, args, arg_reg, call_target);
3067 direct_icalls_enabled (MonoCompile *cfg)
3069 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
3071 if (cfg->compile_llvm && !cfg->llvm_only)
3074 if (cfg->gen_sdb_seq_points || cfg->disable_direct_icalls)
3080 mono_emit_jit_icall_by_info (MonoCompile *cfg, int il_offset, MonoJitICallInfo *info, MonoInst **args)
3083 * Call the jit icall without a wrapper if possible.
3084 * The wrapper is needed for the following reasons:
3085 * - to handle exceptions thrown using mono_raise_exceptions () from the
3086 * icall function. The EH code needs the lmf frame pushed by the
3087 * wrapper to be able to unwind back to managed code.
3088 * - to be able to do stack walks for asynchronously suspended
3089 * threads when debugging.
3091 if (info->no_raise && direct_icalls_enabled (cfg)) {
3095 if (!info->wrapper_method) {
3096 name = g_strdup_printf ("__icall_wrapper_%s", info->name);
3097 info->wrapper_method = mono_marshal_get_icall_wrapper (info->sig, name, info->func, TRUE);
3099 mono_memory_barrier ();
3103 * Inline the wrapper method, which is basically a call to the C icall, and
3104 * an exception check.
3106 costs = inline_method (cfg, info->wrapper_method, NULL,
3107 args, NULL, il_offset, TRUE);
3108 g_assert (costs > 0);
3109 g_assert (!MONO_TYPE_IS_VOID (info->sig->ret));
3113 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
3118 mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig)
3120 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
3121 if ((fsig->pinvoke || LLVM_ENABLED) && !fsig->ret->byref) {
3125 * Native code might return non register sized integers
3126 * without initializing the upper bits.
3128 switch (mono_type_to_load_membase (cfg, fsig->ret)) {
3129 case OP_LOADI1_MEMBASE:
3130 widen_op = OP_ICONV_TO_I1;
3132 case OP_LOADU1_MEMBASE:
3133 widen_op = OP_ICONV_TO_U1;
3135 case OP_LOADI2_MEMBASE:
3136 widen_op = OP_ICONV_TO_I2;
3138 case OP_LOADU2_MEMBASE:
3139 widen_op = OP_ICONV_TO_U2;
3145 if (widen_op != -1) {
3146 int dreg = alloc_preg (cfg);
3149 EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg);
3150 widen->type = ins->type;
3160 get_memcpy_method (void)
3162 static MonoMethod *memcpy_method = NULL;
3163 if (!memcpy_method) {
3164 memcpy_method = mono_class_get_method_from_name (mono_defaults.string_class, "memcpy", 3);
3166 g_error ("Old corlib found. Install a new one");
3168 return memcpy_method;
3172 create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
3174 MonoClassField *field;
3175 gpointer iter = NULL;
3177 while ((field = mono_class_get_fields (klass, &iter))) {
3180 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
3182 foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
3183 if (mini_type_is_reference (mono_field_get_type (field))) {
3184 g_assert ((foffset % SIZEOF_VOID_P) == 0);
3185 *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
3187 MonoClass *field_class = mono_class_from_mono_type (field->type);
3188 if (field_class->has_references)
3189 create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
3195 emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
3197 int card_table_shift_bits;
3198 gpointer card_table_mask;
3200 MonoInst *dummy_use;
3201 int nursery_shift_bits;
3202 size_t nursery_size;
3204 if (!cfg->gen_write_barriers)
3207 card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
3209 mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
3211 if (cfg->backend->have_card_table_wb && !cfg->compile_aot && card_table && nursery_shift_bits > 0 && !COMPILE_LLVM (cfg)) {
3214 MONO_INST_NEW (cfg, wbarrier, OP_CARD_TABLE_WBARRIER);
3215 wbarrier->sreg1 = ptr->dreg;
3216 wbarrier->sreg2 = value->dreg;
3217 MONO_ADD_INS (cfg->cbb, wbarrier);
3218 } else if (card_table && !cfg->compile_aot && !mono_gc_card_table_nursery_check ()) {
3219 int offset_reg = alloc_preg (cfg);
3223 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_UN_IMM, offset_reg, ptr->dreg, card_table_shift_bits);
3224 if (card_table_mask)
3225 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, offset_reg, offset_reg, card_table_mask);
3227 /*We can't use PADD_IMM since the cardtable might end up in high addresses and amd64 doesn't support
3228 * IMM's larger than 32bits.
3230 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
3231 card_reg = ins->dreg;
3233 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, offset_reg, offset_reg, card_reg);
3234 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, offset_reg, 0, 1);
3236 MonoMethod *write_barrier = mono_gc_get_write_barrier ();
3237 mono_emit_method_call (cfg, write_barrier, &ptr, NULL);
3240 EMIT_NEW_DUMMY_USE (cfg, dummy_use, value);
3244 mono_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
3246 int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
3247 unsigned need_wb = 0;
3252 /*types with references can't have alignment smaller than sizeof(void*) */
3253 if (align < SIZEOF_VOID_P)
3256 /*This value cannot be bigger than 32 due to the way we calculate the required wb bitmap.*/
3257 if (size > 32 * SIZEOF_VOID_P)
3260 create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
3262 /* We don't unroll more than 5 stores to avoid code bloat. */
3263 if (size > 5 * SIZEOF_VOID_P) {
3264 /*This is harmless and simplify mono_gc_wbarrier_value_copy_bitmap */
3265 size += (SIZEOF_VOID_P - 1);
3266 size &= ~(SIZEOF_VOID_P - 1);
3268 EMIT_NEW_ICONST (cfg, iargs [2], size);
3269 EMIT_NEW_ICONST (cfg, iargs [3], need_wb);
3270 mono_emit_jit_icall (cfg, mono_gc_wbarrier_value_copy_bitmap, iargs);
3274 destreg = iargs [0]->dreg;
3275 srcreg = iargs [1]->dreg;
3278 dest_ptr_reg = alloc_preg (cfg);
3279 tmp_reg = alloc_preg (cfg);
3282 EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
3284 while (size >= SIZEOF_VOID_P) {
3285 MonoInst *load_inst;
3286 MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
3287 load_inst->dreg = tmp_reg;
3288 load_inst->inst_basereg = srcreg;
3289 load_inst->inst_offset = offset;
3290 MONO_ADD_INS (cfg->cbb, load_inst);
3292 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
3295 emit_write_barrier (cfg, iargs [0], load_inst);
3297 offset += SIZEOF_VOID_P;
3298 size -= SIZEOF_VOID_P;
3301 /*tmp += sizeof (void*)*/
3302 if (size >= SIZEOF_VOID_P) {
3303 NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
3304 MONO_ADD_INS (cfg->cbb, iargs [0]);
3308 /* Those cannot be references since size < sizeof (void*) */
3310 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
3311 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
3317 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
3318 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
3324 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
3325 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
3334 * Emit code to copy a valuetype of type @klass whose address is stored in
3335 * @src->dreg to memory whose address is stored at @dest->dreg.
3338 mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
3340 MonoInst *iargs [4];
3343 MonoMethod *memcpy_method;
3344 MonoInst *size_ins = NULL;
3345 MonoInst *memcpy_ins = NULL;
3349 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
3352 * This check breaks with spilled vars... need to handle it during verification anyway.
3353 * g_assert (klass && klass == src->klass && klass == dest->klass);
3356 if (mini_is_gsharedvt_klass (klass)) {
3358 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3359 memcpy_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
3363 n = mono_class_native_size (klass, &align);
3365 n = mono_class_value_size (klass, &align);
3367 /* if native is true there should be no references in the struct */
3368 if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) {
3369 /* Avoid barriers when storing to the stack */
3370 if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
3371 (dest->opcode == OP_LDADDR))) {
3377 context_used = mini_class_check_context_used (cfg, klass);
3379 /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
3380 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mono_emit_wb_aware_memcpy (cfg, klass, iargs, n, align)) {
3382 } else if (context_used) {
3383 iargs [2] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3385 iargs [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
3386 if (!cfg->compile_aot)
3387 mono_class_compute_gc_descriptor (klass);
3391 mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
3393 mono_emit_jit_icall (cfg, mono_value_copy, iargs);
3398 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 8) {
3399 /* FIXME: Optimize the case when src/dest is OP_LDADDR */
3400 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, n, align);
3405 iargs [2] = size_ins;
3407 EMIT_NEW_ICONST (cfg, iargs [2], n);
3409 memcpy_method = get_memcpy_method ();
3411 mono_emit_calli (cfg, mono_method_signature (memcpy_method), iargs, memcpy_ins, NULL, NULL);
3413 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
3418 get_memset_method (void)
3420 static MonoMethod *memset_method = NULL;
3421 if (!memset_method) {
3422 memset_method = mono_class_get_method_from_name (mono_defaults.string_class, "memset", 3);
3424 g_error ("Old corlib found. Install a new one");
3426 return memset_method;
3430 mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass)
3432 MonoInst *iargs [3];
3435 MonoMethod *memset_method;
3436 MonoInst *size_ins = NULL;
3437 MonoInst *bzero_ins = NULL;
3438 static MonoMethod *bzero_method;
3440 /* FIXME: Optimize this for the case when dest is an LDADDR */
3441 mono_class_init (klass);
3442 if (mini_is_gsharedvt_klass (klass)) {
3443 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3444 bzero_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_BZERO);
3446 bzero_method = mono_class_get_method_from_name (mono_defaults.string_class, "bzero_aligned_1", 2);
3447 g_assert (bzero_method);
3449 iargs [1] = size_ins;
3450 mono_emit_calli (cfg, mono_method_signature (bzero_method), iargs, bzero_ins, NULL, NULL);
3454 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
3456 n = mono_class_value_size (klass, &align);
3458 if (n <= sizeof (gpointer) * 8) {
3459 mini_emit_memset (cfg, dest->dreg, 0, n, 0, align);
3462 memset_method = get_memset_method ();
3464 EMIT_NEW_ICONST (cfg, iargs [1], 0);
3465 EMIT_NEW_ICONST (cfg, iargs [2], n);
3466 mono_emit_method_call (cfg, memset_method, iargs, NULL);
3473 * Emit IR to return either the this pointer for instance method,
3474 * or the mrgctx for static methods.
3477 emit_get_rgctx (MonoCompile *cfg, MonoMethod *method, int context_used)
3479 MonoInst *this_ins = NULL;
3481 g_assert (cfg->gshared);
3483 if (!(method->flags & METHOD_ATTRIBUTE_STATIC) &&
3484 !(context_used & MONO_GENERIC_CONTEXT_USED_METHOD) &&
3485 !method->klass->valuetype)
3486 EMIT_NEW_ARGLOAD (cfg, this_ins, 0);
3488 if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD) {
3489 MonoInst *mrgctx_loc, *mrgctx_var;
3491 g_assert (!this_ins);
3492 g_assert (method->is_inflated && mono_method_get_context (method)->method_inst);
3494 mrgctx_loc = mono_get_vtable_var (cfg);
3495 EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
3498 } else if (method->flags & METHOD_ATTRIBUTE_STATIC || method->klass->valuetype) {
3499 MonoInst *vtable_loc, *vtable_var;
3501 g_assert (!this_ins);
3503 vtable_loc = mono_get_vtable_var (cfg);
3504 EMIT_NEW_TEMPLOAD (cfg, vtable_var, vtable_loc->inst_c0);
3506 if (method->is_inflated && mono_method_get_context (method)->method_inst) {
3507 MonoInst *mrgctx_var = vtable_var;
3510 vtable_reg = alloc_preg (cfg);
3511 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable));
3512 vtable_var->type = STACK_PTR;
3520 vtable_reg = alloc_preg (cfg);
3521 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, vtable_reg, this_ins->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3526 static MonoJumpInfoRgctxEntry *
3527 mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean in_mrgctx, MonoJumpInfoType patch_type, gconstpointer patch_data, MonoRgctxInfoType info_type)
3529 MonoJumpInfoRgctxEntry *res = (MonoJumpInfoRgctxEntry *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry));
3530 res->method = method;
3531 res->in_mrgctx = in_mrgctx;
3532 res->data = (MonoJumpInfo *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
3533 res->data->type = patch_type;
3534 res->data->data.target = patch_data;
3535 res->info_type = info_type;
3540 static inline MonoInst*
3541 emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3543 MonoInst *args [16];
3546 // FIXME: No fastpath since the slot is not a compile time constant
3548 EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_RGCTX_SLOT_INDEX, entry);
3549 if (entry->in_mrgctx)
3550 call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args);
3552 call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args);
3556 * FIXME: This can be called during decompose, which is a problem since it creates
3558 * Also, the fastpath doesn't work since the slot number is dynamically allocated.
3560 int i, slot, depth, index, rgctx_reg, val_reg, res_reg;
3562 MonoBasicBlock *is_null_bb, *end_bb;
3563 MonoInst *res, *ins, *call;
3566 slot = mini_get_rgctx_entry_slot (entry);
3568 mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
3569 index = MONO_RGCTX_SLOT_INDEX (slot);
3571 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
3572 for (depth = 0; ; ++depth) {
3573 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
3575 if (index < size - 1)
3580 NEW_BBLOCK (cfg, end_bb);
3581 NEW_BBLOCK (cfg, is_null_bb);
3584 rgctx_reg = rgctx->dreg;
3586 rgctx_reg = alloc_preg (cfg);
3588 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, rgctx_reg, rgctx->dreg, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
3589 // FIXME: Avoid this check by allocating the table when the vtable is created etc.
3590 NEW_BBLOCK (cfg, is_null_bb);
3592 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
3593 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3596 for (i = 0; i < depth; ++i) {
3597 int array_reg = alloc_preg (cfg);
3599 /* load ptr to next array */
3600 if (mrgctx && i == 0)
3601 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
3603 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, 0);
3604 rgctx_reg = array_reg;
3605 /* is the ptr null? */
3606 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
3607 /* if yes, jump to actual trampoline */
3608 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3612 val_reg = alloc_preg (cfg);
3613 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, val_reg, rgctx_reg, (index + 1) * sizeof (gpointer));
3614 /* is the slot null? */
3615 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, val_reg, 0);
3616 /* if yes, jump to actual trampoline */
3617 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3620 res_reg = alloc_preg (cfg);
3621 MONO_INST_NEW (cfg, ins, OP_MOVE);
3622 ins->dreg = res_reg;
3623 ins->sreg1 = val_reg;
3624 MONO_ADD_INS (cfg->cbb, ins);
3626 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3629 MONO_START_BB (cfg, is_null_bb);
3631 EMIT_NEW_ICONST (cfg, args [1], index);
3633 call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args);
3635 call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args);
3636 MONO_INST_NEW (cfg, ins, OP_MOVE);
3637 ins->dreg = res_reg;
3638 ins->sreg1 = call->dreg;
3639 MONO_ADD_INS (cfg->cbb, ins);
3640 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3642 MONO_START_BB (cfg, end_bb);
3651 * Emit IR to load the value of the rgctx entry ENTRY from the rgctx
3654 static inline MonoInst*
3655 emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3658 return emit_rgctx_fetch_inline (cfg, rgctx, entry);
3660 return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx);
3664 emit_get_rgctx_klass (MonoCompile *cfg, int context_used,
3665 MonoClass *klass, MonoRgctxInfoType rgctx_type)
3667 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_CLASS, klass, rgctx_type);
3668 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3670 return emit_rgctx_fetch (cfg, rgctx, entry);
3674 emit_get_rgctx_sig (MonoCompile *cfg, int context_used,
3675 MonoMethodSignature *sig, MonoRgctxInfoType rgctx_type)
3677 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_SIGNATURE, sig, rgctx_type);
3678 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3680 return emit_rgctx_fetch (cfg, rgctx, entry);
3684 emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used,
3685 MonoMethodSignature *sig, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3687 MonoJumpInfoGSharedVtCall *call_info;
3688 MonoJumpInfoRgctxEntry *entry;
3691 call_info = (MonoJumpInfoGSharedVtCall *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoGSharedVtCall));
3692 call_info->sig = sig;
3693 call_info->method = cmethod;
3695 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_GSHAREDVT_CALL, call_info, rgctx_type);
3696 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3698 return emit_rgctx_fetch (cfg, rgctx, entry);
3702 * emit_get_rgctx_virt_method:
3704 * Return data for method VIRT_METHOD for a receiver of type KLASS.
3707 emit_get_rgctx_virt_method (MonoCompile *cfg, int context_used,
3708 MonoClass *klass, MonoMethod *virt_method, MonoRgctxInfoType rgctx_type)
3710 MonoJumpInfoVirtMethod *info;
3711 MonoJumpInfoRgctxEntry *entry;
3714 info = (MonoJumpInfoVirtMethod *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoVirtMethod));
3715 info->klass = klass;
3716 info->method = virt_method;
3718 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_VIRT_METHOD, info, rgctx_type);
3719 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3721 return emit_rgctx_fetch (cfg, rgctx, entry);
3725 emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used,
3726 MonoMethod *cmethod, MonoGSharedVtMethodInfo *info)
3728 MonoJumpInfoRgctxEntry *entry;
3731 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_GSHAREDVT_METHOD, info, MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO);
3732 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3734 return emit_rgctx_fetch (cfg, rgctx, entry);
3738 * emit_get_rgctx_method:
3740 * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit
3741 * normal constants, else emit a load from the rgctx.
3744 emit_get_rgctx_method (MonoCompile *cfg, int context_used,
3745 MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3747 if (!context_used) {
3750 switch (rgctx_type) {
3751 case MONO_RGCTX_INFO_METHOD:
3752 EMIT_NEW_METHODCONST (cfg, ins, cmethod);
3754 case MONO_RGCTX_INFO_METHOD_RGCTX:
3755 EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod);
3758 g_assert_not_reached ();
3761 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type);
3762 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3764 return emit_rgctx_fetch (cfg, rgctx, entry);
3769 emit_get_rgctx_field (MonoCompile *cfg, int context_used,
3770 MonoClassField *field, MonoRgctxInfoType rgctx_type)
3772 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_FIELD, field, rgctx_type);
3773 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3775 return emit_rgctx_fetch (cfg, rgctx, entry);
3779 get_gsharedvt_info_slot (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3781 MonoGSharedVtMethodInfo *info = cfg->gsharedvt_info;
3782 MonoRuntimeGenericContextInfoTemplate *template_;
3787 for (i = 0; i < info->num_entries; ++i) {
3788 MonoRuntimeGenericContextInfoTemplate *otemplate = &info->entries [i];
3790 if (otemplate->info_type == rgctx_type && otemplate->data == data && rgctx_type != MONO_RGCTX_INFO_LOCAL_OFFSET)
3794 if (info->num_entries == info->count_entries) {
3795 MonoRuntimeGenericContextInfoTemplate *new_entries;
3796 int new_count_entries = info->count_entries ? info->count_entries * 2 : 16;
3798 new_entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * new_count_entries);
3800 memcpy (new_entries, info->entries, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
3801 info->entries = new_entries;
3802 info->count_entries = new_count_entries;
3805 idx = info->num_entries;
3806 template_ = &info->entries [idx];
3807 template_->info_type = rgctx_type;
3808 template_->data = data;
3810 info->num_entries ++;
3816 * emit_get_gsharedvt_info:
3818 * This is similar to emit_get_rgctx_.., but loads the data from the gsharedvt info var instead of calling an rgctx fetch trampoline.
3821 emit_get_gsharedvt_info (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3826 idx = get_gsharedvt_info_slot (cfg, data, rgctx_type);
3827 /* Load info->entries [idx] */
3828 dreg = alloc_preg (cfg);
3829 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, cfg->gsharedvt_info_var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
3835 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type)
3837 return emit_get_gsharedvt_info (cfg, &klass->byval_arg, rgctx_type);
3841 * On return the caller must check @klass for load errors.
3844 emit_class_init (MonoCompile *cfg, MonoClass *klass)
3846 MonoInst *vtable_arg;
3849 context_used = mini_class_check_context_used (cfg, klass);
3852 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
3853 klass, MONO_RGCTX_INFO_VTABLE);
3855 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3859 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
3862 if (!COMPILE_LLVM (cfg) && cfg->backend->have_op_generic_class_init) {
3866 * Using an opcode instead of emitting IR here allows the hiding of the call inside the opcode,
3867 * so this doesn't have to clobber any regs and it doesn't break basic blocks.
3869 MONO_INST_NEW (cfg, ins, OP_GENERIC_CLASS_INIT);
3870 ins->sreg1 = vtable_arg->dreg;
3871 MONO_ADD_INS (cfg->cbb, ins);
3873 static int byte_offset = -1;
3874 static guint8 bitmask;
3875 int bits_reg, inited_reg;
3876 MonoBasicBlock *inited_bb;
3877 MonoInst *args [16];
3879 if (byte_offset < 0)
3880 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
3882 bits_reg = alloc_ireg (cfg);
3883 inited_reg = alloc_ireg (cfg);
3885 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, bits_reg, vtable_arg->dreg, byte_offset);
3886 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, inited_reg, bits_reg, bitmask);
3888 NEW_BBLOCK (cfg, inited_bb);
3890 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, inited_reg, 0);
3891 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, inited_bb);
3893 args [0] = vtable_arg;
3894 mono_emit_jit_icall (cfg, mono_generic_class_init, args);
3896 MONO_START_BB (cfg, inited_bb);
3901 emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_loc, gboolean nonempty_stack)
3905 if (cfg->gen_seq_points && cfg->method == method) {
3906 NEW_SEQ_POINT (cfg, ins, ip - cfg->header->code, intr_loc);
3908 ins->flags |= MONO_INST_NONEMPTY_STACK;
3909 MONO_ADD_INS (cfg->cbb, ins);
3914 save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check)
3916 if (mini_get_debug_options ()->better_cast_details) {
3917 int vtable_reg = alloc_preg (cfg);
3918 int klass_reg = alloc_preg (cfg);
3919 MonoBasicBlock *is_null_bb = NULL;
3921 int to_klass_reg, context_used;
3924 NEW_BBLOCK (cfg, is_null_bb);
3926 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
3927 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3930 tls_get = mono_get_jit_tls_intrinsic (cfg);
3932 fprintf (stderr, "error: --debug=casts not supported on this platform.\n.");
3936 MONO_ADD_INS (cfg->cbb, tls_get);
3937 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3938 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3940 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), klass_reg);
3942 context_used = mini_class_check_context_used (cfg, klass);
3944 MonoInst *class_ins;
3946 class_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3947 to_klass_reg = class_ins->dreg;
3949 to_klass_reg = alloc_preg (cfg);
3950 MONO_EMIT_NEW_CLASSCONST (cfg, to_klass_reg, klass);
3952 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_to), to_klass_reg);
3955 MONO_START_BB (cfg, is_null_bb);
3960 reset_cast_details (MonoCompile *cfg)
3962 /* Reset the variables holding the cast details */
3963 if (mini_get_debug_options ()->better_cast_details) {
3964 MonoInst *tls_get = mono_get_jit_tls_intrinsic (cfg);
3966 MONO_ADD_INS (cfg->cbb, tls_get);
3967 /* It is enough to reset the from field */
3968 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), 0);
3973 * On return the caller must check @array_class for load errors
3976 mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_class)
3978 int vtable_reg = alloc_preg (cfg);
3981 context_used = mini_class_check_context_used (cfg, array_class);
3983 save_cast_details (cfg, array_class, obj->dreg, FALSE);
3985 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3987 if (cfg->opt & MONO_OPT_SHARED) {
3988 int class_reg = alloc_preg (cfg);
3991 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, class_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3992 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, array_class);
3993 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, class_reg, ins->dreg);
3994 } else if (context_used) {
3995 MonoInst *vtable_ins;
3997 vtable_ins = emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE);
3998 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg);
4000 if (cfg->compile_aot) {
4004 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
4006 vt_reg = alloc_preg (cfg);
4007 MONO_EMIT_NEW_VTABLECONST (cfg, vt_reg, vtable);
4008 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vt_reg);
4011 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
4013 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vtable);
4017 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ArrayTypeMismatchException");
4019 reset_cast_details (cfg);
4023 * Handles unbox of a Nullable<T>. If context_used is non zero, then shared
4024 * generic code is generated.
4027 handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used)
4029 MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
4032 MonoInst *rgctx, *addr;
4034 /* FIXME: What if the class is shared? We might not
4035 have to get the address of the method from the
4037 addr = emit_get_rgctx_method (cfg, context_used, method,
4038 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4039 if (cfg->llvm_only && cfg->gsharedvt) {
4040 return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
4042 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4044 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4047 gboolean pass_vtable, pass_mrgctx;
4048 MonoInst *rgctx_arg = NULL;
4050 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4051 g_assert (!pass_mrgctx);
4054 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4057 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4060 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4065 handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_used)
4069 int vtable_reg = alloc_dreg (cfg ,STACK_PTR);
4070 int klass_reg = alloc_dreg (cfg ,STACK_PTR);
4071 int eclass_reg = alloc_dreg (cfg ,STACK_PTR);
4072 int rank_reg = alloc_dreg (cfg ,STACK_I4);
4074 obj_reg = sp [0]->dreg;
4075 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4076 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4078 /* FIXME: generics */
4079 g_assert (klass->rank == 0);
4082 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 0);
4083 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4085 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4086 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, element_class));
4089 MonoInst *element_class;
4091 /* This assertion is from the unboxcast insn */
4092 g_assert (klass->rank == 0);
4094 element_class = emit_get_rgctx_klass (cfg, context_used,
4095 klass, MONO_RGCTX_INFO_ELEMENT_KLASS);
4097 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg);
4098 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4100 save_cast_details (cfg, klass->element_class, obj_reg, FALSE);
4101 mini_emit_class_check (cfg, eclass_reg, klass->element_class);
4102 reset_cast_details (cfg);
4105 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), obj_reg, sizeof (MonoObject));
4106 MONO_ADD_INS (cfg->cbb, add);
4107 add->type = STACK_MP;
4114 handle_unbox_gsharedvt (MonoCompile *cfg, MonoClass *klass, MonoInst *obj)
4116 MonoInst *addr, *klass_inst, *is_ref, *args[16];
4117 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4121 klass_inst = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_KLASS);
4127 args [1] = klass_inst;
4130 obj = mono_emit_jit_icall (cfg, mono_object_castclass_unbox, args);
4132 NEW_BBLOCK (cfg, is_ref_bb);
4133 NEW_BBLOCK (cfg, is_nullable_bb);
4134 NEW_BBLOCK (cfg, end_bb);
4135 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4136 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
4137 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4139 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
4140 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4142 /* This will contain either the address of the unboxed vtype, or an address of the temporary where the ref is stored */
4143 addr_reg = alloc_dreg (cfg, STACK_MP);
4147 NEW_BIALU_IMM (cfg, addr, OP_ADD_IMM, addr_reg, obj->dreg, sizeof (MonoObject));
4148 MONO_ADD_INS (cfg->cbb, addr);
4150 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4153 MONO_START_BB (cfg, is_ref_bb);
4155 /* Save the ref to a temporary */
4156 dreg = alloc_ireg (cfg);
4157 EMIT_NEW_VARLOADA_VREG (cfg, addr, dreg, &klass->byval_arg);
4158 addr->dreg = addr_reg;
4159 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, obj->dreg);
4160 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4163 MONO_START_BB (cfg, is_nullable_bb);
4166 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX);
4167 MonoInst *unbox_call;
4168 MonoMethodSignature *unbox_sig;
4170 unbox_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4171 unbox_sig->ret = &klass->byval_arg;
4172 unbox_sig->param_count = 1;
4173 unbox_sig->params [0] = &mono_defaults.object_class->byval_arg;
4176 unbox_call = emit_llvmonly_calli (cfg, unbox_sig, &obj, addr);
4178 unbox_call = mono_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
4180 EMIT_NEW_VARLOADA_VREG (cfg, addr, unbox_call->dreg, &klass->byval_arg);
4181 addr->dreg = addr_reg;
4184 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4187 MONO_START_BB (cfg, end_bb);
4190 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr_reg, 0);
4196 * Returns NULL and set the cfg exception on error.
4199 handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_used)
4201 MonoInst *iargs [2];
4206 MonoRgctxInfoType rgctx_info;
4207 MonoInst *iargs [2];
4208 gboolean known_instance_size = !mini_is_gsharedvt_klass (klass);
4210 MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box, known_instance_size);
4212 if (cfg->opt & MONO_OPT_SHARED)
4213 rgctx_info = MONO_RGCTX_INFO_KLASS;
4215 rgctx_info = MONO_RGCTX_INFO_VTABLE;
4216 data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
4218 if (cfg->opt & MONO_OPT_SHARED) {
4219 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
4221 alloc_ftn = ves_icall_object_new;
4224 alloc_ftn = ves_icall_object_new_specific;
4227 if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED)) {
4228 if (known_instance_size) {
4229 int size = mono_class_instance_size (klass);
4230 if (size < sizeof (MonoObject))
4231 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
4233 EMIT_NEW_ICONST (cfg, iargs [1], size);
4235 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
4238 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4241 if (cfg->opt & MONO_OPT_SHARED) {
4242 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
4243 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
4245 alloc_ftn = ves_icall_object_new;
4246 } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !klass->generic_class) {
4247 /* This happens often in argument checking code, eg. throw new FooException... */
4248 /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
4249 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
4250 return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
4252 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
4253 MonoMethod *managed_alloc = NULL;
4257 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4258 cfg->exception_ptr = klass;
4262 managed_alloc = mono_gc_get_managed_allocator (klass, for_box, TRUE);
4264 if (managed_alloc) {
4265 int size = mono_class_instance_size (klass);
4266 if (size < sizeof (MonoObject))
4267 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
4269 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4270 EMIT_NEW_ICONST (cfg, iargs [1], size);
4271 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
4273 alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
4275 guint32 lw = vtable->klass->instance_size;
4276 lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
4277 EMIT_NEW_ICONST (cfg, iargs [0], lw);
4278 EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
4281 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4285 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4289 * Returns NULL and set the cfg exception on error.
4292 handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used)
4294 MonoInst *alloc, *ins;
4296 if (mono_class_is_nullable (klass)) {
4297 MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
4300 if (cfg->llvm_only && cfg->gsharedvt) {
4301 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4302 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4303 return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
4305 /* FIXME: What if the class is shared? We might not
4306 have to get the method address from the RGCTX. */
4307 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4308 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4309 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4311 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4314 gboolean pass_vtable, pass_mrgctx;
4315 MonoInst *rgctx_arg = NULL;
4317 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4318 g_assert (!pass_mrgctx);
4321 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4324 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4327 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4331 if (mini_is_gsharedvt_klass (klass)) {
4332 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4333 MonoInst *res, *is_ref, *src_var, *addr;
4336 dreg = alloc_ireg (cfg);
4338 NEW_BBLOCK (cfg, is_ref_bb);
4339 NEW_BBLOCK (cfg, is_nullable_bb);
4340 NEW_BBLOCK (cfg, end_bb);
4341 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4342 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
4343 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4345 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
4346 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4349 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4352 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4353 ins->opcode = OP_STOREV_MEMBASE;
4355 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg);
4356 res->type = STACK_OBJ;
4358 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4361 MONO_START_BB (cfg, is_ref_bb);
4363 /* val is a vtype, so has to load the value manually */
4364 src_var = get_vreg_to_inst (cfg, val->dreg);
4366 src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg);
4367 EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype);
4368 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0);
4369 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4372 MONO_START_BB (cfg, is_nullable_bb);
4375 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass,
4376 MONO_RGCTX_INFO_NULLABLE_CLASS_BOX);
4378 MonoMethodSignature *box_sig;
4381 * klass is Nullable<T>, need to call Nullable<T>.Box () using a gsharedvt signature, but we cannot
4382 * construct that method at JIT time, so have to do things by hand.
4384 box_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4385 box_sig->ret = &mono_defaults.object_class->byval_arg;
4386 box_sig->param_count = 1;
4387 box_sig->params [0] = &klass->byval_arg;
4390 box_call = emit_llvmonly_calli (cfg, box_sig, &val, addr);
4392 box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
4393 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg);
4394 res->type = STACK_OBJ;
4398 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4400 MONO_START_BB (cfg, end_bb);
4404 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4408 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4414 mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used)
4417 MonoGenericContainer *container;
4418 MonoGenericInst *ginst;
4420 if (klass->generic_class) {
4421 container = klass->generic_class->container_class->generic_container;
4422 ginst = klass->generic_class->context.class_inst;
4423 } else if (klass->generic_container && context_used) {
4424 container = klass->generic_container;
4425 ginst = container->context.class_inst;
4430 for (i = 0; i < container->type_argc; ++i) {
4432 if (!(mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT)))
4434 type = ginst->type_argv [i];
4435 if (mini_type_is_reference (type))
4441 static GHashTable* direct_icall_type_hash;
4444 icall_is_direct_callable (MonoCompile *cfg, MonoMethod *cmethod)
4446 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
4447 if (!direct_icalls_enabled (cfg))
4451 * An icall is directly callable if it doesn't directly or indirectly call mono_raise_exception ().
4452 * Whitelist a few icalls for now.
4454 if (!direct_icall_type_hash) {
4455 GHashTable *h = g_hash_table_new (g_str_hash, g_str_equal);
4457 g_hash_table_insert (h, (char*)"Decimal", GUINT_TO_POINTER (1));
4458 g_hash_table_insert (h, (char*)"Number", GUINT_TO_POINTER (1));
4459 g_hash_table_insert (h, (char*)"Buffer", GUINT_TO_POINTER (1));
4460 g_hash_table_insert (h, (char*)"Monitor", GUINT_TO_POINTER (1));
4461 mono_memory_barrier ();
4462 direct_icall_type_hash = h;
4465 if (cmethod->klass == mono_defaults.math_class)
4467 /* No locking needed */
4468 if (cmethod->klass->image == mono_defaults.corlib && g_hash_table_lookup (direct_icall_type_hash, cmethod->klass->name))
4473 #define is_complex_isinst(klass) ((klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || mono_class_is_marshalbyref (klass) || (klass->flags & TYPE_ATTRIBUTE_SEALED) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
4476 emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args)
4478 MonoMethod *mono_castclass;
4481 mono_castclass = mono_marshal_get_castclass_with_cache ();
4483 save_cast_details (cfg, klass, args [0]->dreg, TRUE);
4484 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
4485 reset_cast_details (cfg);
4491 get_castclass_cache_idx (MonoCompile *cfg)
4493 /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
4494 cfg->castclass_cache_index ++;
4495 return (cfg->method_index << 16) | cfg->castclass_cache_index;
4499 emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass)
4508 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
4511 idx = get_castclass_cache_idx (cfg);
4512 args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
4514 /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/
4515 return emit_castclass_with_cache (cfg, klass, args);
4519 * Returns NULL and set the cfg exception on error.
4522 handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, guint8 *ip, int *inline_costs)
4524 MonoBasicBlock *is_null_bb;
4525 int obj_reg = src->dreg;
4526 int vtable_reg = alloc_preg (cfg);
4528 MonoInst *klass_inst = NULL, *res;
4530 if (src->opcode == OP_PCONST && src->inst_p0 == 0)
4533 context_used = mini_class_check_context_used (cfg, klass);
4535 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
4536 res = emit_castclass_with_cache_nonshared (cfg, src, klass);
4537 (*inline_costs) += 2;
4539 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
4540 MonoMethod *mono_castclass;
4541 MonoInst *iargs [1];
4544 mono_castclass = mono_marshal_get_castclass (klass);
4547 save_cast_details (cfg, klass, src->dreg, TRUE);
4548 costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass),
4549 iargs, ip, cfg->real_offset, TRUE);
4550 reset_cast_details (cfg);
4551 CHECK_CFG_EXCEPTION;
4552 g_assert (costs > 0);
4554 cfg->real_offset += 5;
4556 (*inline_costs) += costs;
4564 if (mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4565 MonoInst *cache_ins;
4567 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4572 /* klass - it's the second element of the cache entry*/
4573 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4576 args [2] = cache_ins;
4578 return emit_castclass_with_cache (cfg, klass, args);
4581 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4584 NEW_BBLOCK (cfg, is_null_bb);
4586 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4587 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
4589 save_cast_details (cfg, klass, obj_reg, FALSE);
4591 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4592 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4593 mini_emit_iface_cast (cfg, vtable_reg, klass, NULL, NULL);
4595 int klass_reg = alloc_preg (cfg);
4597 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4599 if (!klass->rank && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4600 /* the remoting code is broken, access the class for now */
4601 if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4602 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4604 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4605 cfg->exception_ptr = klass;
4608 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4610 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4611 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4613 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4615 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4616 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
4620 MONO_START_BB (cfg, is_null_bb);
4622 reset_cast_details (cfg);
4631 * Returns NULL and set the cfg exception on error.
4634 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
4637 MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
4638 int obj_reg = src->dreg;
4639 int vtable_reg = alloc_preg (cfg);
4640 int res_reg = alloc_ireg_ref (cfg);
4641 MonoInst *klass_inst = NULL;
4646 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4647 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
4648 MonoInst *cache_ins;
4650 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4655 /* klass - it's the second element of the cache entry*/
4656 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4659 args [2] = cache_ins;
4661 return mono_emit_method_call (cfg, mono_isinst, args, NULL);
4664 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4667 NEW_BBLOCK (cfg, is_null_bb);
4668 NEW_BBLOCK (cfg, false_bb);
4669 NEW_BBLOCK (cfg, end_bb);
4671 /* Do the assignment at the beginning, so the other assignment can be if converted */
4672 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg);
4673 ins->type = STACK_OBJ;
4676 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4677 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
4679 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4681 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4682 g_assert (!context_used);
4683 /* the is_null_bb target simply copies the input register to the output */
4684 mini_emit_iface_cast (cfg, vtable_reg, klass, false_bb, is_null_bb);
4686 int klass_reg = alloc_preg (cfg);
4689 int rank_reg = alloc_preg (cfg);
4690 int eclass_reg = alloc_preg (cfg);
4692 g_assert (!context_used);
4693 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4694 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
4695 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4696 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4697 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
4698 if (klass->cast_class == mono_defaults.object_class) {
4699 int parent_reg = alloc_preg (cfg);
4700 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
4701 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, is_null_bb);
4702 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4703 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4704 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
4705 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, is_null_bb);
4706 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4707 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4708 } else if (klass->cast_class == mono_defaults.enum_class) {
4709 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4710 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4711 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
4712 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4714 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY)) {
4715 /* Check that the object is a vector too */
4716 int bounds_reg = alloc_preg (cfg);
4717 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
4718 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
4719 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4722 /* the is_null_bb target simply copies the input register to the output */
4723 mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4725 } else if (mono_class_is_nullable (klass)) {
4726 g_assert (!context_used);
4727 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4728 /* the is_null_bb target simply copies the input register to the output */
4729 mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb);
4731 if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4732 g_assert (!context_used);
4733 /* the remoting code is broken, access the class for now */
4734 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4735 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4737 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4738 cfg->exception_ptr = klass;
4741 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4743 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4744 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4746 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4747 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
4749 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4750 /* the is_null_bb target simply copies the input register to the output */
4751 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
4756 MONO_START_BB (cfg, false_bb);
4758 MONO_EMIT_NEW_PCONST (cfg, res_reg, 0);
4759 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4761 MONO_START_BB (cfg, is_null_bb);
4763 MONO_START_BB (cfg, end_bb);
4769 handle_cisinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4771 /* This opcode takes as input an object reference and a class, and returns:
4772 0) if the object is an instance of the class,
4773 1) if the object is not instance of the class,
4774 2) if the object is a proxy whose type cannot be determined */
4777 #ifndef DISABLE_REMOTING
4778 MonoBasicBlock *true_bb, *false_bb, *false2_bb, *end_bb, *no_proxy_bb, *interface_fail_bb;
4780 MonoBasicBlock *true_bb, *false_bb, *end_bb;
4782 int obj_reg = src->dreg;
4783 int dreg = alloc_ireg (cfg);
4785 #ifndef DISABLE_REMOTING
4786 int klass_reg = alloc_preg (cfg);
4789 NEW_BBLOCK (cfg, true_bb);
4790 NEW_BBLOCK (cfg, false_bb);
4791 NEW_BBLOCK (cfg, end_bb);
4792 #ifndef DISABLE_REMOTING
4793 NEW_BBLOCK (cfg, false2_bb);
4794 NEW_BBLOCK (cfg, no_proxy_bb);
4797 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4798 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
4800 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4801 #ifndef DISABLE_REMOTING
4802 NEW_BBLOCK (cfg, interface_fail_bb);
4805 tmp_reg = alloc_preg (cfg);
4806 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4807 #ifndef DISABLE_REMOTING
4808 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, true_bb);
4809 MONO_START_BB (cfg, interface_fail_bb);
4810 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4812 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb);
4814 tmp_reg = alloc_preg (cfg);
4815 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4816 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4817 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false2_bb);
4819 mini_emit_iface_cast (cfg, tmp_reg, klass, false_bb, true_bb);
4822 #ifndef DISABLE_REMOTING
4823 tmp_reg = alloc_preg (cfg);
4824 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4825 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4827 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4828 tmp_reg = alloc_preg (cfg);
4829 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4830 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4832 tmp_reg = alloc_preg (cfg);
4833 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4834 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4835 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4837 mini_emit_isninst_cast (cfg, klass_reg, klass, false2_bb, true_bb);
4838 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false2_bb);
4840 MONO_START_BB (cfg, no_proxy_bb);
4842 mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, true_bb);
4844 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
4848 MONO_START_BB (cfg, false_bb);
4850 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4851 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4853 #ifndef DISABLE_REMOTING
4854 MONO_START_BB (cfg, false2_bb);
4856 MONO_EMIT_NEW_ICONST (cfg, dreg, 2);
4857 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4860 MONO_START_BB (cfg, true_bb);
4862 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4864 MONO_START_BB (cfg, end_bb);
4867 MONO_INST_NEW (cfg, ins, OP_ICONST);
4869 ins->type = STACK_I4;
4875 handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4877 /* This opcode takes as input an object reference and a class, and returns:
4878 0) if the object is an instance of the class,
4879 1) if the object is a proxy whose type cannot be determined
4880 an InvalidCastException exception is thrown otherwhise*/
4883 #ifndef DISABLE_REMOTING
4884 MonoBasicBlock *end_bb, *ok_result_bb, *no_proxy_bb, *interface_fail_bb, *fail_1_bb;
4886 MonoBasicBlock *ok_result_bb;
4888 int obj_reg = src->dreg;
4889 int dreg = alloc_ireg (cfg);
4890 int tmp_reg = alloc_preg (cfg);
4892 #ifndef DISABLE_REMOTING
4893 int klass_reg = alloc_preg (cfg);
4894 NEW_BBLOCK (cfg, end_bb);
4897 NEW_BBLOCK (cfg, ok_result_bb);
4899 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4900 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb);
4902 save_cast_details (cfg, klass, obj_reg, FALSE);
4904 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4905 #ifndef DISABLE_REMOTING
4906 NEW_BBLOCK (cfg, interface_fail_bb);
4908 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4909 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, ok_result_bb);
4910 MONO_START_BB (cfg, interface_fail_bb);
4911 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4913 mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class);
4915 tmp_reg = alloc_preg (cfg);
4916 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4917 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4918 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
4920 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4921 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4923 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4924 mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
4925 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, ok_result_bb);
4928 #ifndef DISABLE_REMOTING
4929 NEW_BBLOCK (cfg, no_proxy_bb);
4931 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4932 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4933 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4935 tmp_reg = alloc_preg (cfg);
4936 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4937 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4939 tmp_reg = alloc_preg (cfg);
4940 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4941 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4942 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4944 NEW_BBLOCK (cfg, fail_1_bb);
4946 mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb);
4948 MONO_START_BB (cfg, fail_1_bb);
4950 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4951 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4953 MONO_START_BB (cfg, no_proxy_bb);
4955 mini_emit_castclass (cfg, obj_reg, klass_reg, klass, ok_result_bb);
4957 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
4961 MONO_START_BB (cfg, ok_result_bb);
4963 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4965 #ifndef DISABLE_REMOTING
4966 MONO_START_BB (cfg, end_bb);
4970 MONO_INST_NEW (cfg, ins, OP_ICONST);
4972 ins->type = STACK_I4;
4977 static G_GNUC_UNUSED MonoInst*
4978 handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag)
4980 MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg);
4981 guint32 load_opc = mono_type_to_load_membase (cfg, enum_type);
4984 switch (enum_type->type) {
4987 #if SIZEOF_REGISTER == 8
4999 MonoInst *load, *and_, *cmp, *ceq;
5000 int enum_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
5001 int and_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
5002 int dest_reg = alloc_ireg (cfg);
5004 EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0);
5005 EMIT_NEW_BIALU (cfg, and_, is_i4 ? OP_IAND : OP_LAND, and_reg, enum_reg, enum_flag->dreg);
5006 EMIT_NEW_BIALU (cfg, cmp, is_i4 ? OP_ICOMPARE : OP_LCOMPARE, -1, and_reg, enum_flag->dreg);
5007 EMIT_NEW_UNALU (cfg, ceq, is_i4 ? OP_ICEQ : OP_LCEQ, dest_reg, -1);
5009 ceq->type = STACK_I4;
5012 load = mono_decompose_opcode (cfg, load);
5013 and_ = mono_decompose_opcode (cfg, and_);
5014 cmp = mono_decompose_opcode (cfg, cmp);
5015 ceq = mono_decompose_opcode (cfg, ceq);
5023 * Returns NULL and set the cfg exception on error.
5025 static G_GNUC_UNUSED MonoInst*
5026 handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used, gboolean virtual_)
5030 gpointer trampoline;
5031 MonoInst *obj, *method_ins, *tramp_ins;
5035 if (virtual_ && !cfg->llvm_only) {
5036 MonoMethod *invoke = mono_get_delegate_invoke (klass);
5039 if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
5043 obj = handle_alloc (cfg, klass, FALSE, mono_class_check_context_used (klass));
5047 /* Inline the contents of mono_delegate_ctor */
5049 /* Set target field */
5050 /* Optimize away setting of NULL target */
5051 if (!(target->opcode == OP_PCONST && target->inst_p0 == 0)) {
5052 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
5053 if (cfg->gen_write_barriers) {
5054 dreg = alloc_preg (cfg);
5055 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target));
5056 emit_write_barrier (cfg, ptr, target);
5060 /* Set method field */
5061 method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
5062 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
5065 * To avoid looking up the compiled code belonging to the target method
5066 * in mono_delegate_trampoline (), we allocate a per-domain memory slot to
5067 * store it, and we fill it after the method has been compiled.
5069 if (!method->dynamic && !(cfg->opt & MONO_OPT_SHARED)) {
5070 MonoInst *code_slot_ins;
5073 code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE);
5075 domain = mono_domain_get ();
5076 mono_domain_lock (domain);
5077 if (!domain_jit_info (domain)->method_code_hash)
5078 domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
5079 code_slot = (guint8 **)g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
5081 code_slot = (guint8 **)mono_domain_alloc0 (domain, sizeof (gpointer));
5082 g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
5084 mono_domain_unlock (domain);
5086 code_slot_ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHOD_CODE_SLOT, method);
5088 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
5091 if (cfg->llvm_only) {
5092 MonoInst *args [16];
5097 args [2] = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
5098 mono_emit_jit_icall (cfg, mono_llvmonly_init_delegate_virtual, args);
5101 mono_emit_jit_icall (cfg, mono_llvmonly_init_delegate, args);
5107 if (cfg->compile_aot) {
5108 MonoDelegateClassMethodPair *del_tramp;
5110 del_tramp = (MonoDelegateClassMethodPair *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoDelegateClassMethodPair));
5111 del_tramp->klass = klass;
5112 del_tramp->method = context_used ? NULL : method;
5113 del_tramp->is_virtual = virtual_;
5114 EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
5117 trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
5119 trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
5120 EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
5123 /* Set invoke_impl field */
5125 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
5127 dreg = alloc_preg (cfg);
5128 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
5129 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
5131 dreg = alloc_preg (cfg);
5132 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
5133 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
5136 dreg = alloc_preg (cfg);
5137 MONO_EMIT_NEW_ICONST (cfg, dreg, virtual_ ? 1 : 0);
5138 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_is_virtual), dreg);
5140 /* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
5146 handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip)
5148 MonoJitICallInfo *info;
5150 /* Need to register the icall so it gets an icall wrapper */
5151 info = mono_get_array_new_va_icall (rank);
5153 cfg->flags |= MONO_CFG_HAS_VARARGS;
5155 /* mono_array_new_va () needs a vararg calling convention */
5156 cfg->exception_message = g_strdup ("array-new");
5157 cfg->disable_llvm = TRUE;
5159 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
5160 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp);
5164 * handle_constrained_gsharedvt_call:
5166 * Handle constrained calls where the receiver is a gsharedvt type.
5167 * Return the instruction representing the call. Set the cfg exception on failure.
5170 handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, MonoClass *constrained_class,
5171 gboolean *ref_emit_widen)
5173 MonoInst *ins = NULL;
5174 gboolean emit_widen = *ref_emit_widen;
5177 * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype.
5178 * 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
5179 * pack the arguments into an array, and do the rest of the work in in an icall.
5181 if (((cmethod->klass == mono_defaults.object_class) || (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) &&
5182 (MONO_TYPE_IS_VOID (fsig->ret) || MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_IS_REFERENCE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mini_is_gsharedvt_type (fsig->ret)) &&
5183 (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]))))) {
5184 MonoInst *args [16];
5187 * This case handles calls to
5188 * - object:ToString()/Equals()/GetHashCode(),
5189 * - System.IComparable<T>:CompareTo()
5190 * - System.IEquatable<T>:Equals ()
5191 * plus some simple interface calls enough to support AsyncTaskMethodBuilder.
5195 if (mono_method_check_context_used (cmethod))
5196 args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD);
5198 EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
5199 args [2] = emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_class), constrained_class, MONO_RGCTX_INFO_KLASS);
5201 /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
5202 if (fsig->hasthis && fsig->param_count) {
5203 /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */
5204 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
5205 ins->dreg = alloc_preg (cfg);
5206 ins->inst_imm = fsig->param_count * sizeof (mgreg_t);
5207 MONO_ADD_INS (cfg->cbb, ins);
5210 if (mini_is_gsharedvt_type (fsig->params [0])) {
5211 int addr_reg, deref_arg_reg;
5213 ins = emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE);
5214 deref_arg_reg = alloc_preg (cfg);
5215 /* deref_arg = BOX_TYPE != MONO_GSHAREDVT_BOX_TYPE_VTYPE */
5216 EMIT_NEW_BIALU_IMM (cfg, args [3], OP_ISUB_IMM, deref_arg_reg, ins->dreg, 1);
5218 EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]);
5219 addr_reg = ins->dreg;
5220 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg);
5222 EMIT_NEW_ICONST (cfg, args [3], 0);
5223 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg);
5226 EMIT_NEW_ICONST (cfg, args [3], 0);
5227 EMIT_NEW_ICONST (cfg, args [4], 0);
5229 ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args);
5232 if (mini_is_gsharedvt_type (fsig->ret)) {
5233 ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins);
5234 } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) {
5238 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject));
5239 MONO_ADD_INS (cfg->cbb, add);
5241 NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0);
5242 MONO_ADD_INS (cfg->cbb, ins);
5243 /* ins represents the call result */
5246 GSHAREDVT_FAILURE (CEE_CALLVIRT);
5249 *ref_emit_widen = emit_widen;
5258 mono_emit_load_got_addr (MonoCompile *cfg)
5260 MonoInst *getaddr, *dummy_use;
5262 if (!cfg->got_var || cfg->got_var_allocated)
5265 MONO_INST_NEW (cfg, getaddr, OP_LOAD_GOTADDR);
5266 getaddr->cil_code = cfg->header->code;
5267 getaddr->dreg = cfg->got_var->dreg;
5269 /* Add it to the start of the first bblock */
5270 if (cfg->bb_entry->code) {
5271 getaddr->next = cfg->bb_entry->code;
5272 cfg->bb_entry->code = getaddr;
5275 MONO_ADD_INS (cfg->bb_entry, getaddr);
5277 cfg->got_var_allocated = TRUE;
5280 * Add a dummy use to keep the got_var alive, since real uses might
5281 * only be generated by the back ends.
5282 * Add it to end_bblock, so the variable's lifetime covers the whole
5284 * It would be better to make the usage of the got var explicit in all
5285 * cases when the backend needs it (i.e. calls, throw etc.), so this
5286 * wouldn't be needed.
5288 NEW_DUMMY_USE (cfg, dummy_use, cfg->got_var);
5289 MONO_ADD_INS (cfg->bb_exit, dummy_use);
5292 static int inline_limit;
5293 static gboolean inline_limit_inited;
5296 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
5298 MonoMethodHeaderSummary header;
5300 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5301 MonoMethodSignature *sig = mono_method_signature (method);
5305 if (cfg->disable_inline)
5310 if (cfg->inline_depth > 10)
5313 if (!mono_method_get_header_summary (method, &header))
5316 /*runtime, icall and pinvoke are checked by summary call*/
5317 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
5318 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
5319 (mono_class_is_marshalbyref (method->klass)) ||
5323 /* also consider num_locals? */
5324 /* Do the size check early to avoid creating vtables */
5325 if (!inline_limit_inited) {
5326 if (g_getenv ("MONO_INLINELIMIT"))
5327 inline_limit = atoi (g_getenv ("MONO_INLINELIMIT"));
5329 inline_limit = INLINE_LENGTH_LIMIT;
5330 inline_limit_inited = TRUE;
5332 if (header.code_size >= inline_limit && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
5336 * if we can initialize the class of the method right away, we do,
5337 * otherwise we don't allow inlining if the class needs initialization,
5338 * since it would mean inserting a call to mono_runtime_class_init()
5339 * inside the inlined code
5341 if (!(cfg->opt & MONO_OPT_SHARED)) {
5342 /* The AggressiveInlining hint is a good excuse to force that cctor to run. */
5343 if (method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) {
5344 vtable = mono_class_vtable (cfg->domain, method->klass);
5347 if (!cfg->compile_aot) {
5349 if (!mono_runtime_class_init_full (vtable, &error)) {
5350 mono_error_cleanup (&error);
5354 } else if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5355 if (cfg->run_cctors && method->klass->has_cctor) {
5356 /*FIXME it would easier and lazier to just use mono_class_try_get_vtable */
5357 if (!method->klass->runtime_info)
5358 /* No vtable created yet */
5360 vtable = mono_class_vtable (cfg->domain, method->klass);
5363 /* This makes so that inline cannot trigger */
5364 /* .cctors: too many apps depend on them */
5365 /* running with a specific order... */
5366 if (! vtable->initialized)
5369 if (!mono_runtime_class_init_full (vtable, &error)) {
5370 mono_error_cleanup (&error);
5374 } else if (mono_class_needs_cctor_run (method->klass, NULL)) {
5375 if (!method->klass->runtime_info)
5376 /* No vtable created yet */
5378 vtable = mono_class_vtable (cfg->domain, method->klass);
5381 if (!vtable->initialized)
5386 * If we're compiling for shared code
5387 * the cctor will need to be run at aot method load time, for example,
5388 * or at the end of the compilation of the inlining method.
5390 if (mono_class_needs_cctor_run (method->klass, NULL) && !((method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)))
5394 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5395 if (mono_arch_is_soft_float ()) {
5397 if (sig->ret && sig->ret->type == MONO_TYPE_R4)
5399 for (i = 0; i < sig->param_count; ++i)
5400 if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_R4)
5405 if (g_list_find (cfg->dont_inline, method))
5412 mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoClass *klass, MonoVTable *vtable)
5414 if (!cfg->compile_aot) {
5416 if (vtable->initialized)
5420 if (klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5421 if (cfg->method == method)
5425 if (!mono_class_needs_cctor_run (klass, method))
5428 if (! (method->flags & METHOD_ATTRIBUTE_STATIC) && (klass == method->klass))
5429 /* The initialization is already done before the method is called */
5436 mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
5440 int mult_reg, add_reg, array_reg, index_reg, index2_reg;
5443 if (mini_is_gsharedvt_variable_klass (klass)) {
5446 mono_class_init (klass);
5447 size = mono_class_array_element_size (klass);
5450 mult_reg = alloc_preg (cfg);
5451 array_reg = arr->dreg;
5452 index_reg = index->dreg;
5454 #if SIZEOF_REGISTER == 8
5455 /* The array reg is 64 bits but the index reg is only 32 */
5456 if (COMPILE_LLVM (cfg)) {
5458 index2_reg = index_reg;
5460 index2_reg = alloc_preg (cfg);
5461 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
5464 if (index->type == STACK_I8) {
5465 index2_reg = alloc_preg (cfg);
5466 MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg);
5468 index2_reg = index_reg;
5473 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
5475 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5476 if (size == 1 || size == 2 || size == 4 || size == 8) {
5477 static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 };
5479 EMIT_NEW_X86_LEA (cfg, ins, array_reg, index2_reg, fast_log2 [size], MONO_STRUCT_OFFSET (MonoArray, vector));
5480 ins->klass = mono_class_get_element_class (klass);
5481 ins->type = STACK_MP;
5487 add_reg = alloc_ireg_mp (cfg);
5490 MonoInst *rgctx_ins;
5493 g_assert (cfg->gshared);
5494 context_used = mini_class_check_context_used (cfg, klass);
5495 g_assert (context_used);
5496 rgctx_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE);
5497 MONO_EMIT_NEW_BIALU (cfg, OP_IMUL, mult_reg, index2_reg, rgctx_ins->dreg);
5499 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MUL_IMM, mult_reg, index2_reg, size);
5501 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, array_reg, mult_reg);
5502 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5503 ins->klass = mono_class_get_element_class (klass);
5504 ins->type = STACK_MP;
5505 MONO_ADD_INS (cfg->cbb, ins);
5511 mini_emit_ldelema_2_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index_ins1, MonoInst *index_ins2)
5513 int bounds_reg = alloc_preg (cfg);
5514 int add_reg = alloc_ireg_mp (cfg);
5515 int mult_reg = alloc_preg (cfg);
5516 int mult2_reg = alloc_preg (cfg);
5517 int low1_reg = alloc_preg (cfg);
5518 int low2_reg = alloc_preg (cfg);
5519 int high1_reg = alloc_preg (cfg);
5520 int high2_reg = alloc_preg (cfg);
5521 int realidx1_reg = alloc_preg (cfg);
5522 int realidx2_reg = alloc_preg (cfg);
5523 int sum_reg = alloc_preg (cfg);
5524 int index1, index2, tmpreg;
5528 mono_class_init (klass);
5529 size = mono_class_array_element_size (klass);
5531 index1 = index_ins1->dreg;
5532 index2 = index_ins2->dreg;
5534 #if SIZEOF_REGISTER == 8
5535 /* The array reg is 64 bits but the index reg is only 32 */
5536 if (COMPILE_LLVM (cfg)) {
5539 tmpreg = alloc_preg (cfg);
5540 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index1);
5542 tmpreg = alloc_preg (cfg);
5543 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index2);
5547 // FIXME: Do we need to do something here for i8 indexes, like in ldelema_1_ins ?
5551 /* range checking */
5552 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg,
5553 arr->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5555 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low1_reg,
5556 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5557 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx1_reg, index1, low1_reg);
5558 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high1_reg,
5559 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5560 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high1_reg, realidx1_reg);
5561 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5563 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low2_reg,
5564 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5565 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx2_reg, index2, low2_reg);
5566 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high2_reg,
5567 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5568 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high2_reg, realidx2_reg);
5569 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5571 MONO_EMIT_NEW_BIALU (cfg, OP_PMUL, mult_reg, high2_reg, realidx1_reg);
5572 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, mult_reg, realidx2_reg);
5573 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PMUL_IMM, mult2_reg, sum_reg, size);
5574 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult2_reg, arr->dreg);
5575 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5577 ins->type = STACK_MP;
5579 MONO_ADD_INS (cfg->cbb, ins);
5585 mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set)
5589 MonoMethod *addr_method;
5591 MonoClass *eclass = cmethod->klass->element_class;
5593 rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
5596 return mini_emit_ldelema_1_ins (cfg, eclass, sp [0], sp [1], TRUE);
5598 /* emit_ldelema_2 depends on OP_LMUL */
5599 if (!cfg->backend->emulate_mul_div && rank == 2 && (cfg->opt & MONO_OPT_INTRINS) && !mini_is_gsharedvt_variable_klass (eclass)) {
5600 return mini_emit_ldelema_2_ins (cfg, eclass, sp [0], sp [1], sp [2]);
5603 if (mini_is_gsharedvt_variable_klass (eclass))
5606 element_size = mono_class_array_element_size (eclass);
5607 addr_method = mono_marshal_get_array_address (rank, element_size);
5608 addr = mono_emit_method_call (cfg, addr_method, sp, NULL);
5613 static MonoBreakPolicy
5614 always_insert_breakpoint (MonoMethod *method)
5616 return MONO_BREAK_POLICY_ALWAYS;
5619 static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
5622 * mono_set_break_policy:
5623 * policy_callback: the new callback function
5625 * Allow embedders to decide wherther to actually obey breakpoint instructions
5626 * (both break IL instructions and Debugger.Break () method calls), for example
5627 * to not allow an app to be aborted by a perfectly valid IL opcode when executing
5628 * untrusted or semi-trusted code.
5630 * @policy_callback will be called every time a break point instruction needs to
5631 * be inserted with the method argument being the method that calls Debugger.Break()
5632 * or has the IL break instruction. The callback should return #MONO_BREAK_POLICY_NEVER
5633 * if it wants the breakpoint to not be effective in the given method.
5634 * #MONO_BREAK_POLICY_ALWAYS is the default.
5637 mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
5639 if (policy_callback)
5640 break_policy_func = policy_callback;
5642 break_policy_func = always_insert_breakpoint;
5646 should_insert_brekpoint (MonoMethod *method) {
5647 switch (break_policy_func (method)) {
5648 case MONO_BREAK_POLICY_ALWAYS:
5650 case MONO_BREAK_POLICY_NEVER:
5652 case MONO_BREAK_POLICY_ON_DBG:
5653 g_warning ("mdb no longer supported");
5656 g_warning ("Incorrect value returned from break policy callback");
5661 /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
5663 emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5665 MonoInst *addr, *store, *load;
5666 MonoClass *eklass = mono_class_from_mono_type (fsig->params [2]);
5668 /* the bounds check is already done by the callers */
5669 addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5671 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, args [2]->dreg, 0);
5672 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, addr->dreg, 0, load->dreg);
5673 if (mini_type_is_reference (fsig->params [2]))
5674 emit_write_barrier (cfg, addr, load);
5676 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
5677 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
5684 generic_class_is_reference_type (MonoCompile *cfg, MonoClass *klass)
5686 return mini_type_is_reference (&klass->byval_arg);
5690 emit_array_store (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, gboolean safety_checks)
5692 if (safety_checks && generic_class_is_reference_type (cfg, klass) &&
5693 !(sp [2]->opcode == OP_PCONST && sp [2]->inst_p0 == NULL)) {
5694 MonoClass *obj_array = mono_array_class_get_cached (mono_defaults.object_class, 1);
5695 MonoMethod *helper = mono_marshal_get_virtual_stelemref (obj_array);
5696 MonoInst *iargs [3];
5699 mono_class_setup_vtable (obj_array);
5700 g_assert (helper->slot);
5702 if (sp [0]->type != STACK_OBJ)
5704 if (sp [2]->type != STACK_OBJ)
5711 return mono_emit_method_call (cfg, helper, iargs, sp [0]);
5715 if (mini_is_gsharedvt_variable_klass (klass)) {
5718 // FIXME-VT: OP_ICONST optimization
5719 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
5720 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5721 ins->opcode = OP_STOREV_MEMBASE;
5722 } else if (sp [1]->opcode == OP_ICONST) {
5723 int array_reg = sp [0]->dreg;
5724 int index_reg = sp [1]->dreg;
5725 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
5727 if (SIZEOF_REGISTER == 8 && COMPILE_LLVM (cfg))
5728 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, index_reg);
5731 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
5732 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset, sp [2]->dreg);
5734 MonoInst *addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], safety_checks);
5735 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5736 if (generic_class_is_reference_type (cfg, klass))
5737 emit_write_barrier (cfg, addr, sp [2]);
5744 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5749 eklass = mono_class_from_mono_type (fsig->params [2]);
5751 eklass = mono_class_from_mono_type (fsig->ret);
5754 return emit_array_store (cfg, eklass, args, FALSE);
5756 MonoInst *ins, *addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5757 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &eklass->byval_arg, addr->dreg, 0);
5763 is_unsafe_mov_compatible (MonoCompile *cfg, MonoClass *param_klass, MonoClass *return_klass)
5766 int param_size, return_size;
5768 param_klass = mono_class_from_mono_type (mini_get_underlying_type (¶m_klass->byval_arg));
5769 return_klass = mono_class_from_mono_type (mini_get_underlying_type (&return_klass->byval_arg));
5771 if (cfg->verbose_level > 3)
5772 printf ("[UNSAFE-MOV-INTRISIC] %s <- %s\n", return_klass->name, param_klass->name);
5774 //Don't allow mixing reference types with value types
5775 if (param_klass->valuetype != return_klass->valuetype) {
5776 if (cfg->verbose_level > 3)
5777 printf ("[UNSAFE-MOV-INTRISIC]\tone of the args is a valuetype and the other is not\n");
5781 if (!param_klass->valuetype) {
5782 if (cfg->verbose_level > 3)
5783 printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
5788 if (param_klass->has_references || return_klass->has_references)
5791 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
5792 if ((MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
5793 (!MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg))) {
5794 if (cfg->verbose_level > 3)
5795 printf ("[UNSAFE-MOV-INTRISIC]\tmixing structs and scalars\n");
5799 if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
5800 return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8) {
5801 if (cfg->verbose_level > 3)
5802 printf ("[UNSAFE-MOV-INTRISIC]\tfloat or double are not supported\n");
5806 param_size = mono_class_value_size (param_klass, &align);
5807 return_size = mono_class_value_size (return_klass, &align);
5809 //We can do it if sizes match
5810 if (param_size == return_size) {
5811 if (cfg->verbose_level > 3)
5812 printf ("[UNSAFE-MOV-INTRISIC]\tsame size\n");
5816 //No simple way to handle struct if sizes don't match
5817 if (MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg)) {
5818 if (cfg->verbose_level > 3)
5819 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch and type is a struct\n");
5824 * Same reg size category.
5825 * A quick note on why we don't require widening here.
5826 * The intrinsic is "R Array.UnsafeMov<S,R> (S s)".
5828 * Since the source value comes from a function argument, the JIT will already have
5829 * the value in a VREG and performed any widening needed before (say, when loading from a field).
5831 if (param_size <= 4 && return_size <= 4) {
5832 if (cfg->verbose_level > 3)
5833 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch but both are of the same reg class\n");
5841 emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args)
5843 MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
5844 MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
5846 if (mini_is_gsharedvt_variable_type (fsig->ret))
5849 //Valuetypes that are semantically equivalent or numbers than can be widened to
5850 if (is_unsafe_mov_compatible (cfg, param_klass, return_klass))
5853 //Arrays of valuetypes that are semantically equivalent
5854 if (param_klass->rank == 1 && return_klass->rank == 1 && is_unsafe_mov_compatible (cfg, param_klass->element_class, return_klass->element_class))
5861 mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5863 #ifdef MONO_ARCH_SIMD_INTRINSICS
5864 MonoInst *ins = NULL;
5866 if (cfg->opt & MONO_OPT_SIMD) {
5867 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
5873 return mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
5877 emit_memory_barrier (MonoCompile *cfg, int kind)
5879 MonoInst *ins = NULL;
5880 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
5881 MONO_ADD_INS (cfg->cbb, ins);
5882 ins->backend.memory_barrier_kind = kind;
5888 llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5890 MonoInst *ins = NULL;
5893 /* The LLVM backend supports these intrinsics */
5894 if (cmethod->klass == mono_defaults.math_class) {
5895 if (strcmp (cmethod->name, "Sin") == 0) {
5897 } else if (strcmp (cmethod->name, "Cos") == 0) {
5899 } else if (strcmp (cmethod->name, "Sqrt") == 0) {
5901 } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
5905 if (opcode && fsig->param_count == 1) {
5906 MONO_INST_NEW (cfg, ins, opcode);
5907 ins->type = STACK_R8;
5908 ins->dreg = mono_alloc_freg (cfg);
5909 ins->sreg1 = args [0]->dreg;
5910 MONO_ADD_INS (cfg->cbb, ins);
5914 if (cfg->opt & MONO_OPT_CMOV) {
5915 if (strcmp (cmethod->name, "Min") == 0) {
5916 if (fsig->params [0]->type == MONO_TYPE_I4)
5918 if (fsig->params [0]->type == MONO_TYPE_U4)
5919 opcode = OP_IMIN_UN;
5920 else if (fsig->params [0]->type == MONO_TYPE_I8)
5922 else if (fsig->params [0]->type == MONO_TYPE_U8)
5923 opcode = OP_LMIN_UN;
5924 } else if (strcmp (cmethod->name, "Max") == 0) {
5925 if (fsig->params [0]->type == MONO_TYPE_I4)
5927 if (fsig->params [0]->type == MONO_TYPE_U4)
5928 opcode = OP_IMAX_UN;
5929 else if (fsig->params [0]->type == MONO_TYPE_I8)
5931 else if (fsig->params [0]->type == MONO_TYPE_U8)
5932 opcode = OP_LMAX_UN;
5936 if (opcode && fsig->param_count == 2) {
5937 MONO_INST_NEW (cfg, ins, opcode);
5938 ins->type = fsig->params [0]->type == MONO_TYPE_I4 ? STACK_I4 : STACK_I8;
5939 ins->dreg = mono_alloc_ireg (cfg);
5940 ins->sreg1 = args [0]->dreg;
5941 ins->sreg2 = args [1]->dreg;
5942 MONO_ADD_INS (cfg->cbb, ins);
5950 mini_emit_inst_for_sharable_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5952 if (cmethod->klass == mono_defaults.array_class) {
5953 if (strcmp (cmethod->name, "UnsafeStore") == 0)
5954 return emit_array_unsafe_access (cfg, fsig, args, TRUE);
5955 else if (strcmp (cmethod->name, "UnsafeLoad") == 0)
5956 return emit_array_unsafe_access (cfg, fsig, args, FALSE);
5957 else if (strcmp (cmethod->name, "UnsafeMov") == 0)
5958 return emit_array_unsafe_mov (cfg, fsig, args);
5965 mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5967 MonoInst *ins = NULL;
5969 MonoClass *runtime_helpers_class = mono_class_get_runtime_helpers_class ();
5971 if (cmethod->klass == mono_defaults.string_class) {
5972 if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count + fsig->hasthis == 2) {
5973 int dreg = alloc_ireg (cfg);
5974 int index_reg = alloc_preg (cfg);
5975 int add_reg = alloc_preg (cfg);
5977 #if SIZEOF_REGISTER == 8
5978 if (COMPILE_LLVM (cfg)) {
5979 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, args [1]->dreg);
5981 /* The array reg is 64 bits but the index reg is only 32 */
5982 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index_reg, args [1]->dreg);
5985 index_reg = args [1]->dreg;
5987 MONO_EMIT_BOUNDS_CHECK (cfg, args [0]->dreg, MonoString, length, index_reg);
5989 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5990 EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, MONO_STRUCT_OFFSET (MonoString, chars));
5991 add_reg = ins->dreg;
5992 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5995 int mult_reg = alloc_preg (cfg);
5996 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, index_reg, 1);
5997 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
5998 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5999 add_reg, MONO_STRUCT_OFFSET (MonoString, chars));
6001 type_from_op (cfg, ins, NULL, NULL);
6003 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
6004 int dreg = alloc_ireg (cfg);
6005 /* Decompose later to allow more optimizations */
6006 EMIT_NEW_UNALU (cfg, ins, OP_STRLEN, dreg, args [0]->dreg);
6007 ins->type = STACK_I4;
6008 ins->flags |= MONO_INST_FAULT;
6009 cfg->cbb->has_array_access = TRUE;
6010 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
6015 } else if (cmethod->klass == mono_defaults.object_class) {
6016 if (strcmp (cmethod->name, "GetType") == 0 && fsig->param_count + fsig->hasthis == 1) {
6017 int dreg = alloc_ireg_ref (cfg);
6018 int vt_reg = alloc_preg (cfg);
6019 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
6020 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, MONO_STRUCT_OFFSET (MonoVTable, type));
6021 type_from_op (cfg, ins, NULL, NULL);
6024 } else if (!cfg->backend->emulate_mul_div && strcmp (cmethod->name, "InternalGetHashCode") == 0 && fsig->param_count == 1 && !mono_gc_is_moving ()) {
6025 int dreg = alloc_ireg (cfg);
6026 int t1 = alloc_ireg (cfg);
6028 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, t1, args [0]->dreg, 3);
6029 EMIT_NEW_BIALU_IMM (cfg, ins, OP_MUL_IMM, dreg, t1, 2654435761u);
6030 ins->type = STACK_I4;
6033 } else if (strcmp (cmethod->name, ".ctor") == 0 && fsig->param_count == 0) {
6034 MONO_INST_NEW (cfg, ins, OP_NOP);
6035 MONO_ADD_INS (cfg->cbb, ins);
6039 } else if (cmethod->klass == mono_defaults.array_class) {
6040 if (strcmp (cmethod->name, "GetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
6041 return emit_array_generic_access (cfg, fsig, args, FALSE);
6042 else if (strcmp (cmethod->name, "SetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
6043 return emit_array_generic_access (cfg, fsig, args, TRUE);
6045 #ifndef MONO_BIG_ARRAYS
6047 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
6050 else if (((strcmp (cmethod->name, "GetLength") == 0 && fsig->param_count + fsig->hasthis == 2) ||
6051 (strcmp (cmethod->name, "GetLowerBound") == 0 && fsig->param_count + fsig->hasthis == 2)) &&
6052 args [1]->opcode == OP_ICONST && args [1]->inst_c0 == 0) {
6053 int dreg = alloc_ireg (cfg);
6054 int bounds_reg = alloc_ireg_mp (cfg);
6055 MonoBasicBlock *end_bb, *szarray_bb;
6056 gboolean get_length = strcmp (cmethod->name, "GetLength") == 0;
6058 NEW_BBLOCK (cfg, end_bb);
6059 NEW_BBLOCK (cfg, szarray_bb);
6061 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOAD_MEMBASE, bounds_reg,
6062 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
6063 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
6064 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, szarray_bb);
6065 /* Non-szarray case */
6067 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6068 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
6070 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6071 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
6072 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
6073 MONO_START_BB (cfg, szarray_bb);
6076 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6077 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
6079 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6080 MONO_START_BB (cfg, end_bb);
6082 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, dreg, dreg);
6083 ins->type = STACK_I4;
6089 if (cmethod->name [0] != 'g')
6092 if (strcmp (cmethod->name, "get_Rank") == 0 && fsig->param_count + fsig->hasthis == 1) {
6093 int dreg = alloc_ireg (cfg);
6094 int vtable_reg = alloc_preg (cfg);
6095 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg,
6096 args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
6097 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg,
6098 vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
6099 type_from_op (cfg, ins, NULL, NULL);
6102 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
6103 int dreg = alloc_ireg (cfg);
6105 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6106 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
6107 type_from_op (cfg, ins, NULL, NULL);
6112 } else if (cmethod->klass == runtime_helpers_class) {
6113 if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) {
6114 EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars));
6118 } else if (cmethod->klass == mono_defaults.monitor_class) {
6119 gboolean is_enter = FALSE;
6121 if (!strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1)
6126 * To make async stack traces work, icalls which can block should have a wrapper.
6127 * For Monitor.Enter, emit two calls: a fastpath which doesn't have a wrapper, and a slowpath, which does.
6129 MonoBasicBlock *end_bb;
6131 NEW_BBLOCK (cfg, end_bb);
6133 ins = mono_emit_jit_icall (cfg, (gpointer)mono_monitor_enter_fast, args);
6134 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, ins->dreg, 0);
6135 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, end_bb);
6136 ins = mono_emit_jit_icall (cfg, (gpointer)mono_monitor_enter, args);
6137 MONO_START_BB (cfg, end_bb);
6140 } else if (cmethod->klass == mono_defaults.thread_class) {
6141 if (strcmp (cmethod->name, "SpinWait_nop") == 0 && fsig->param_count == 0) {
6142 MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP);
6143 MONO_ADD_INS (cfg->cbb, ins);
6145 } else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0) {
6146 return emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6147 } else if (!strcmp (cmethod->name, "VolatileRead") && fsig->param_count == 1) {
6149 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6151 if (fsig->params [0]->type == MONO_TYPE_I1)
6152 opcode = OP_LOADI1_MEMBASE;
6153 else if (fsig->params [0]->type == MONO_TYPE_U1)
6154 opcode = OP_LOADU1_MEMBASE;
6155 else if (fsig->params [0]->type == MONO_TYPE_I2)
6156 opcode = OP_LOADI2_MEMBASE;
6157 else if (fsig->params [0]->type == MONO_TYPE_U2)
6158 opcode = OP_LOADU2_MEMBASE;
6159 else if (fsig->params [0]->type == MONO_TYPE_I4)
6160 opcode = OP_LOADI4_MEMBASE;
6161 else if (fsig->params [0]->type == MONO_TYPE_U4)
6162 opcode = OP_LOADU4_MEMBASE;
6163 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
6164 opcode = OP_LOADI8_MEMBASE;
6165 else if (fsig->params [0]->type == MONO_TYPE_R4)
6166 opcode = OP_LOADR4_MEMBASE;
6167 else if (fsig->params [0]->type == MONO_TYPE_R8)
6168 opcode = OP_LOADR8_MEMBASE;
6169 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
6170 opcode = OP_LOAD_MEMBASE;
6173 MONO_INST_NEW (cfg, ins, opcode);
6174 ins->inst_basereg = args [0]->dreg;
6175 ins->inst_offset = 0;
6176 MONO_ADD_INS (cfg->cbb, ins);
6178 switch (fsig->params [0]->type) {
6185 ins->dreg = mono_alloc_ireg (cfg);
6186 ins->type = STACK_I4;
6190 ins->dreg = mono_alloc_lreg (cfg);
6191 ins->type = STACK_I8;
6195 ins->dreg = mono_alloc_ireg (cfg);
6196 #if SIZEOF_REGISTER == 8
6197 ins->type = STACK_I8;
6199 ins->type = STACK_I4;
6204 ins->dreg = mono_alloc_freg (cfg);
6205 ins->type = STACK_R8;
6208 g_assert (mini_type_is_reference (fsig->params [0]));
6209 ins->dreg = mono_alloc_ireg_ref (cfg);
6210 ins->type = STACK_OBJ;
6214 if (opcode == OP_LOADI8_MEMBASE)
6215 ins = mono_decompose_opcode (cfg, ins);
6217 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6221 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
6223 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6225 if (fsig->params [0]->type == MONO_TYPE_I1 || fsig->params [0]->type == MONO_TYPE_U1)
6226 opcode = OP_STOREI1_MEMBASE_REG;
6227 else if (fsig->params [0]->type == MONO_TYPE_I2 || fsig->params [0]->type == MONO_TYPE_U2)
6228 opcode = OP_STOREI2_MEMBASE_REG;
6229 else if (fsig->params [0]->type == MONO_TYPE_I4 || fsig->params [0]->type == MONO_TYPE_U4)
6230 opcode = OP_STOREI4_MEMBASE_REG;
6231 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
6232 opcode = OP_STOREI8_MEMBASE_REG;
6233 else if (fsig->params [0]->type == MONO_TYPE_R4)
6234 opcode = OP_STORER4_MEMBASE_REG;
6235 else if (fsig->params [0]->type == MONO_TYPE_R8)
6236 opcode = OP_STORER8_MEMBASE_REG;
6237 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
6238 opcode = OP_STORE_MEMBASE_REG;
6241 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6243 MONO_INST_NEW (cfg, ins, opcode);
6244 ins->sreg1 = args [1]->dreg;
6245 ins->inst_destbasereg = args [0]->dreg;
6246 ins->inst_offset = 0;
6247 MONO_ADD_INS (cfg->cbb, ins);
6249 if (opcode == OP_STOREI8_MEMBASE_REG)
6250 ins = mono_decompose_opcode (cfg, ins);
6255 } else if (cmethod->klass->image == mono_defaults.corlib &&
6256 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6257 (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
6260 #if SIZEOF_REGISTER == 8
6261 if (!cfg->llvm_only && strcmp (cmethod->name, "Read") == 0 && fsig->param_count == 1 && (fsig->params [0]->type == MONO_TYPE_I8)) {
6262 if (!cfg->llvm_only && mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8)) {
6263 MONO_INST_NEW (cfg, ins, OP_ATOMIC_LOAD_I8);
6264 ins->dreg = mono_alloc_preg (cfg);
6265 ins->sreg1 = args [0]->dreg;
6266 ins->type = STACK_I8;
6267 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_SEQ;
6268 MONO_ADD_INS (cfg->cbb, ins);
6272 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6274 /* 64 bit reads are already atomic */
6275 MONO_INST_NEW (cfg, load_ins, OP_LOADI8_MEMBASE);
6276 load_ins->dreg = mono_alloc_preg (cfg);
6277 load_ins->inst_basereg = args [0]->dreg;
6278 load_ins->inst_offset = 0;
6279 load_ins->type = STACK_I8;
6280 MONO_ADD_INS (cfg->cbb, load_ins);
6282 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6289 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
6290 MonoInst *ins_iconst;
6293 if (fsig->params [0]->type == MONO_TYPE_I4) {
6294 opcode = OP_ATOMIC_ADD_I4;
6295 cfg->has_atomic_add_i4 = TRUE;
6297 #if SIZEOF_REGISTER == 8
6298 else if (fsig->params [0]->type == MONO_TYPE_I8)
6299 opcode = OP_ATOMIC_ADD_I8;
6302 if (!mono_arch_opcode_supported (opcode))
6304 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6305 ins_iconst->inst_c0 = 1;
6306 ins_iconst->dreg = mono_alloc_ireg (cfg);
6307 MONO_ADD_INS (cfg->cbb, ins_iconst);
6309 MONO_INST_NEW (cfg, ins, opcode);
6310 ins->dreg = mono_alloc_ireg (cfg);
6311 ins->inst_basereg = args [0]->dreg;
6312 ins->inst_offset = 0;
6313 ins->sreg2 = ins_iconst->dreg;
6314 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6315 MONO_ADD_INS (cfg->cbb, ins);
6317 } else if (strcmp (cmethod->name, "Decrement") == 0 && fsig->param_count == 1) {
6318 MonoInst *ins_iconst;
6321 if (fsig->params [0]->type == MONO_TYPE_I4) {
6322 opcode = OP_ATOMIC_ADD_I4;
6323 cfg->has_atomic_add_i4 = TRUE;
6325 #if SIZEOF_REGISTER == 8
6326 else if (fsig->params [0]->type == MONO_TYPE_I8)
6327 opcode = OP_ATOMIC_ADD_I8;
6330 if (!mono_arch_opcode_supported (opcode))
6332 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6333 ins_iconst->inst_c0 = -1;
6334 ins_iconst->dreg = mono_alloc_ireg (cfg);
6335 MONO_ADD_INS (cfg->cbb, ins_iconst);
6337 MONO_INST_NEW (cfg, ins, opcode);
6338 ins->dreg = mono_alloc_ireg (cfg);
6339 ins->inst_basereg = args [0]->dreg;
6340 ins->inst_offset = 0;
6341 ins->sreg2 = ins_iconst->dreg;
6342 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6343 MONO_ADD_INS (cfg->cbb, ins);
6345 } else if (strcmp (cmethod->name, "Add") == 0 && fsig->param_count == 2) {
6348 if (fsig->params [0]->type == MONO_TYPE_I4) {
6349 opcode = OP_ATOMIC_ADD_I4;
6350 cfg->has_atomic_add_i4 = TRUE;
6352 #if SIZEOF_REGISTER == 8
6353 else if (fsig->params [0]->type == MONO_TYPE_I8)
6354 opcode = OP_ATOMIC_ADD_I8;
6357 if (!mono_arch_opcode_supported (opcode))
6359 MONO_INST_NEW (cfg, ins, opcode);
6360 ins->dreg = mono_alloc_ireg (cfg);
6361 ins->inst_basereg = args [0]->dreg;
6362 ins->inst_offset = 0;
6363 ins->sreg2 = args [1]->dreg;
6364 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6365 MONO_ADD_INS (cfg->cbb, ins);
6368 else if (strcmp (cmethod->name, "Exchange") == 0 && fsig->param_count == 2) {
6369 MonoInst *f2i = NULL, *i2f;
6370 guint32 opcode, f2i_opcode, i2f_opcode;
6371 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6372 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6374 if (fsig->params [0]->type == MONO_TYPE_I4 ||
6375 fsig->params [0]->type == MONO_TYPE_R4) {
6376 opcode = OP_ATOMIC_EXCHANGE_I4;
6377 f2i_opcode = OP_MOVE_F_TO_I4;
6378 i2f_opcode = OP_MOVE_I4_TO_F;
6379 cfg->has_atomic_exchange_i4 = TRUE;
6381 #if SIZEOF_REGISTER == 8
6383 fsig->params [0]->type == MONO_TYPE_I8 ||
6384 fsig->params [0]->type == MONO_TYPE_R8 ||
6385 fsig->params [0]->type == MONO_TYPE_I) {
6386 opcode = OP_ATOMIC_EXCHANGE_I8;
6387 f2i_opcode = OP_MOVE_F_TO_I8;
6388 i2f_opcode = OP_MOVE_I8_TO_F;
6391 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I) {
6392 opcode = OP_ATOMIC_EXCHANGE_I4;
6393 cfg->has_atomic_exchange_i4 = TRUE;
6399 if (!mono_arch_opcode_supported (opcode))
6403 /* TODO: Decompose these opcodes instead of bailing here. */
6404 if (COMPILE_SOFT_FLOAT (cfg))
6407 MONO_INST_NEW (cfg, f2i, f2i_opcode);
6408 f2i->dreg = mono_alloc_ireg (cfg);
6409 f2i->sreg1 = args [1]->dreg;
6410 if (f2i_opcode == OP_MOVE_F_TO_I4)
6411 f2i->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6412 MONO_ADD_INS (cfg->cbb, f2i);
6415 MONO_INST_NEW (cfg, ins, opcode);
6416 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg);
6417 ins->inst_basereg = args [0]->dreg;
6418 ins->inst_offset = 0;
6419 ins->sreg2 = is_float ? f2i->dreg : args [1]->dreg;
6420 MONO_ADD_INS (cfg->cbb, ins);
6422 switch (fsig->params [0]->type) {
6424 ins->type = STACK_I4;
6427 ins->type = STACK_I8;
6430 #if SIZEOF_REGISTER == 8
6431 ins->type = STACK_I8;
6433 ins->type = STACK_I4;
6438 ins->type = STACK_R8;
6441 g_assert (mini_type_is_reference (fsig->params [0]));
6442 ins->type = STACK_OBJ;
6447 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6448 i2f->dreg = mono_alloc_freg (cfg);
6449 i2f->sreg1 = ins->dreg;
6450 i2f->type = STACK_R8;
6451 if (i2f_opcode == OP_MOVE_I4_TO_F)
6452 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6453 MONO_ADD_INS (cfg->cbb, i2f);
6458 if (cfg->gen_write_barriers && is_ref)
6459 emit_write_barrier (cfg, args [0], args [1]);
6461 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 3) {
6462 MonoInst *f2i_new = NULL, *f2i_cmp = NULL, *i2f;
6463 guint32 opcode, f2i_opcode, i2f_opcode;
6464 gboolean is_ref = mini_type_is_reference (fsig->params [1]);
6465 gboolean is_float = fsig->params [1]->type == MONO_TYPE_R4 || fsig->params [1]->type == MONO_TYPE_R8;
6467 if (fsig->params [1]->type == MONO_TYPE_I4 ||
6468 fsig->params [1]->type == MONO_TYPE_R4) {
6469 opcode = OP_ATOMIC_CAS_I4;
6470 f2i_opcode = OP_MOVE_F_TO_I4;
6471 i2f_opcode = OP_MOVE_I4_TO_F;
6472 cfg->has_atomic_cas_i4 = TRUE;
6474 #if SIZEOF_REGISTER == 8
6476 fsig->params [1]->type == MONO_TYPE_I8 ||
6477 fsig->params [1]->type == MONO_TYPE_R8 ||
6478 fsig->params [1]->type == MONO_TYPE_I) {
6479 opcode = OP_ATOMIC_CAS_I8;
6480 f2i_opcode = OP_MOVE_F_TO_I8;
6481 i2f_opcode = OP_MOVE_I8_TO_F;
6484 else if (is_ref || fsig->params [1]->type == MONO_TYPE_I) {
6485 opcode = OP_ATOMIC_CAS_I4;
6486 cfg->has_atomic_cas_i4 = TRUE;
6492 if (!mono_arch_opcode_supported (opcode))
6496 /* TODO: Decompose these opcodes instead of bailing here. */
6497 if (COMPILE_SOFT_FLOAT (cfg))
6500 MONO_INST_NEW (cfg, f2i_new, f2i_opcode);
6501 f2i_new->dreg = mono_alloc_ireg (cfg);
6502 f2i_new->sreg1 = args [1]->dreg;
6503 if (f2i_opcode == OP_MOVE_F_TO_I4)
6504 f2i_new->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6505 MONO_ADD_INS (cfg->cbb, f2i_new);
6507 MONO_INST_NEW (cfg, f2i_cmp, f2i_opcode);
6508 f2i_cmp->dreg = mono_alloc_ireg (cfg);
6509 f2i_cmp->sreg1 = args [2]->dreg;
6510 if (f2i_opcode == OP_MOVE_F_TO_I4)
6511 f2i_cmp->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6512 MONO_ADD_INS (cfg->cbb, f2i_cmp);
6515 MONO_INST_NEW (cfg, ins, opcode);
6516 ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
6517 ins->sreg1 = args [0]->dreg;
6518 ins->sreg2 = is_float ? f2i_new->dreg : args [1]->dreg;
6519 ins->sreg3 = is_float ? f2i_cmp->dreg : args [2]->dreg;
6520 MONO_ADD_INS (cfg->cbb, ins);
6522 switch (fsig->params [1]->type) {
6524 ins->type = STACK_I4;
6527 ins->type = STACK_I8;
6530 #if SIZEOF_REGISTER == 8
6531 ins->type = STACK_I8;
6533 ins->type = STACK_I4;
6537 ins->type = cfg->r4_stack_type;
6540 ins->type = STACK_R8;
6543 g_assert (mini_type_is_reference (fsig->params [1]));
6544 ins->type = STACK_OBJ;
6549 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6550 i2f->dreg = mono_alloc_freg (cfg);
6551 i2f->sreg1 = ins->dreg;
6552 i2f->type = STACK_R8;
6553 if (i2f_opcode == OP_MOVE_I4_TO_F)
6554 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6555 MONO_ADD_INS (cfg->cbb, i2f);
6560 if (cfg->gen_write_barriers && is_ref)
6561 emit_write_barrier (cfg, args [0], args [1]);
6563 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 4 &&
6564 fsig->params [1]->type == MONO_TYPE_I4) {
6565 MonoInst *cmp, *ceq;
6567 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4))
6570 /* int32 r = CAS (location, value, comparand); */
6571 MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
6572 ins->dreg = alloc_ireg (cfg);
6573 ins->sreg1 = args [0]->dreg;
6574 ins->sreg2 = args [1]->dreg;
6575 ins->sreg3 = args [2]->dreg;
6576 ins->type = STACK_I4;
6577 MONO_ADD_INS (cfg->cbb, ins);
6579 /* bool result = r == comparand; */
6580 MONO_INST_NEW (cfg, cmp, OP_ICOMPARE);
6581 cmp->sreg1 = ins->dreg;
6582 cmp->sreg2 = args [2]->dreg;
6583 cmp->type = STACK_I4;
6584 MONO_ADD_INS (cfg->cbb, cmp);
6586 MONO_INST_NEW (cfg, ceq, OP_ICEQ);
6587 ceq->dreg = alloc_ireg (cfg);
6588 ceq->type = STACK_I4;
6589 MONO_ADD_INS (cfg->cbb, ceq);
6591 /* *success = result; */
6592 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, args [3]->dreg, 0, ceq->dreg);
6594 cfg->has_atomic_cas_i4 = TRUE;
6596 else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0)
6597 ins = emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6601 } else if (cmethod->klass->image == mono_defaults.corlib &&
6602 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6603 (strcmp (cmethod->klass->name, "Volatile") == 0)) {
6606 if (!cfg->llvm_only && !strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
6608 MonoType *t = fsig->params [0];
6610 gboolean is_float = t->type == MONO_TYPE_R4 || t->type == MONO_TYPE_R8;
6612 g_assert (t->byref);
6613 /* t is a byref type, so the reference check is more complicated */
6614 is_ref = mini_type_is_reference (&mono_class_from_mono_type (t)->byval_arg);
6615 if (t->type == MONO_TYPE_I1)
6616 opcode = OP_ATOMIC_LOAD_I1;
6617 else if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_BOOLEAN)
6618 opcode = OP_ATOMIC_LOAD_U1;
6619 else if (t->type == MONO_TYPE_I2)
6620 opcode = OP_ATOMIC_LOAD_I2;
6621 else if (t->type == MONO_TYPE_U2)
6622 opcode = OP_ATOMIC_LOAD_U2;
6623 else if (t->type == MONO_TYPE_I4)
6624 opcode = OP_ATOMIC_LOAD_I4;
6625 else if (t->type == MONO_TYPE_U4)
6626 opcode = OP_ATOMIC_LOAD_U4;
6627 else if (t->type == MONO_TYPE_R4)
6628 opcode = OP_ATOMIC_LOAD_R4;
6629 else if (t->type == MONO_TYPE_R8)
6630 opcode = OP_ATOMIC_LOAD_R8;
6631 #if SIZEOF_REGISTER == 8
6632 else if (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_I)
6633 opcode = OP_ATOMIC_LOAD_I8;
6634 else if (is_ref || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U)
6635 opcode = OP_ATOMIC_LOAD_U8;
6637 else if (t->type == MONO_TYPE_I)
6638 opcode = OP_ATOMIC_LOAD_I4;
6639 else if (is_ref || t->type == MONO_TYPE_U)
6640 opcode = OP_ATOMIC_LOAD_U4;
6644 if (!mono_arch_opcode_supported (opcode))
6647 MONO_INST_NEW (cfg, ins, opcode);
6648 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : (is_float ? mono_alloc_freg (cfg) : mono_alloc_ireg (cfg));
6649 ins->sreg1 = args [0]->dreg;
6650 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_ACQ;
6651 MONO_ADD_INS (cfg->cbb, ins);
6654 case MONO_TYPE_BOOLEAN:
6661 ins->type = STACK_I4;
6665 ins->type = STACK_I8;
6669 #if SIZEOF_REGISTER == 8
6670 ins->type = STACK_I8;
6672 ins->type = STACK_I4;
6676 ins->type = cfg->r4_stack_type;
6679 ins->type = STACK_R8;
6683 ins->type = STACK_OBJ;
6689 if (!cfg->llvm_only && !strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
6691 MonoType *t = fsig->params [0];
6694 g_assert (t->byref);
6695 is_ref = mini_type_is_reference (&mono_class_from_mono_type (t)->byval_arg);
6696 if (t->type == MONO_TYPE_I1)
6697 opcode = OP_ATOMIC_STORE_I1;
6698 else if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_BOOLEAN)
6699 opcode = OP_ATOMIC_STORE_U1;
6700 else if (t->type == MONO_TYPE_I2)
6701 opcode = OP_ATOMIC_STORE_I2;
6702 else if (t->type == MONO_TYPE_U2)
6703 opcode = OP_ATOMIC_STORE_U2;
6704 else if (t->type == MONO_TYPE_I4)
6705 opcode = OP_ATOMIC_STORE_I4;
6706 else if (t->type == MONO_TYPE_U4)
6707 opcode = OP_ATOMIC_STORE_U4;
6708 else if (t->type == MONO_TYPE_R4)
6709 opcode = OP_ATOMIC_STORE_R4;
6710 else if (t->type == MONO_TYPE_R8)
6711 opcode = OP_ATOMIC_STORE_R8;
6712 #if SIZEOF_REGISTER == 8
6713 else if (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_I)
6714 opcode = OP_ATOMIC_STORE_I8;
6715 else if (is_ref || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U)
6716 opcode = OP_ATOMIC_STORE_U8;
6718 else if (t->type == MONO_TYPE_I)
6719 opcode = OP_ATOMIC_STORE_I4;
6720 else if (is_ref || t->type == MONO_TYPE_U)
6721 opcode = OP_ATOMIC_STORE_U4;
6725 if (!mono_arch_opcode_supported (opcode))
6728 MONO_INST_NEW (cfg, ins, opcode);
6729 ins->dreg = args [0]->dreg;
6730 ins->sreg1 = args [1]->dreg;
6731 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_REL;
6732 MONO_ADD_INS (cfg->cbb, ins);
6734 if (cfg->gen_write_barriers && is_ref)
6735 emit_write_barrier (cfg, args [0], args [1]);
6741 } else if (cmethod->klass->image == mono_defaults.corlib &&
6742 (strcmp (cmethod->klass->name_space, "System.Diagnostics") == 0) &&
6743 (strcmp (cmethod->klass->name, "Debugger") == 0)) {
6744 if (!strcmp (cmethod->name, "Break") && fsig->param_count == 0) {
6745 if (should_insert_brekpoint (cfg->method)) {
6746 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
6748 MONO_INST_NEW (cfg, ins, OP_NOP);
6749 MONO_ADD_INS (cfg->cbb, ins);
6753 } else if (cmethod->klass->image == mono_defaults.corlib &&
6754 (strcmp (cmethod->klass->name_space, "System") == 0) &&
6755 (strcmp (cmethod->klass->name, "Environment") == 0)) {
6756 if (!strcmp (cmethod->name, "get_IsRunningOnWindows") && fsig->param_count == 0) {
6758 EMIT_NEW_ICONST (cfg, ins, 1);
6760 EMIT_NEW_ICONST (cfg, ins, 0);
6763 } else if (cmethod->klass->image == mono_defaults.corlib &&
6764 (strcmp (cmethod->klass->name_space, "System.Reflection") == 0) &&
6765 (strcmp (cmethod->klass->name, "Assembly") == 0)) {
6766 if (cfg->llvm_only && !strcmp (cmethod->name, "GetExecutingAssembly")) {
6767 /* No stack walks are currently available, so implement this as an intrinsic */
6768 MonoInst *assembly_ins;
6770 EMIT_NEW_AOTCONST (cfg, assembly_ins, MONO_PATCH_INFO_IMAGE, cfg->method->klass->image);
6771 ins = mono_emit_jit_icall (cfg, mono_get_assembly_object, &assembly_ins);
6774 } else if (cmethod->klass->image == mono_defaults.corlib &&
6775 (strcmp (cmethod->klass->name_space, "System.Reflection") == 0) &&
6776 (strcmp (cmethod->klass->name, "MethodBase") == 0)) {
6777 if (cfg->llvm_only && !strcmp (cmethod->name, "GetCurrentMethod")) {
6778 /* No stack walks are currently available, so implement this as an intrinsic */
6779 MonoInst *method_ins;
6780 MonoMethod *declaring = cfg->method;
6782 /* This returns the declaring generic method */
6783 if (declaring->is_inflated)
6784 declaring = ((MonoMethodInflated*)cfg->method)->declaring;
6785 EMIT_NEW_AOTCONST (cfg, method_ins, MONO_PATCH_INFO_METHODCONST, declaring);
6786 ins = mono_emit_jit_icall (cfg, mono_get_method_object, &method_ins);
6787 cfg->no_inline = TRUE;
6788 if (cfg->method != cfg->current_method)
6789 inline_failure (cfg, "MethodBase:GetCurrentMethod ()");
6792 } else if (cmethod->klass == mono_defaults.math_class) {
6794 * There is general branchless code for Min/Max, but it does not work for
6796 * http://everything2.com/?node_id=1051618
6798 } else if (((!strcmp (cmethod->klass->image->assembly->aname.name, "MonoMac") ||
6799 !strcmp (cmethod->klass->image->assembly->aname.name, "monotouch")) &&
6800 !strcmp (cmethod->klass->name_space, "XamCore.ObjCRuntime") &&
6801 !strcmp (cmethod->klass->name, "Selector")) ||
6802 (!strcmp (cmethod->klass->image->assembly->aname.name, "Xamarin.iOS") &&
6803 !strcmp (cmethod->klass->name_space, "ObjCRuntime") &&
6804 !strcmp (cmethod->klass->name, "Selector"))
6806 if (cfg->backend->have_objc_get_selector &&
6807 !strcmp (cmethod->name, "GetHandle") && fsig->param_count == 1 &&
6808 (args [0]->opcode == OP_GOT_ENTRY || args [0]->opcode == OP_AOTCONST) &&
6809 cfg->compile_aot && !cfg->llvm_only) {
6811 MonoJumpInfoToken *ji;
6816 cfg->exception_message = g_strdup ("GetHandle");
6817 cfg->disable_llvm = TRUE;
6819 if (args [0]->opcode == OP_GOT_ENTRY) {
6820 pi = (MonoInst *)args [0]->inst_p1;
6821 g_assert (pi->opcode == OP_PATCH_INFO);
6822 g_assert (GPOINTER_TO_INT (pi->inst_p1) == MONO_PATCH_INFO_LDSTR);
6823 ji = (MonoJumpInfoToken *)pi->inst_p0;
6825 g_assert (GPOINTER_TO_INT (args [0]->inst_p1) == MONO_PATCH_INFO_LDSTR);
6826 ji = (MonoJumpInfoToken *)args [0]->inst_p0;
6829 NULLIFY_INS (args [0]);
6832 s = mono_ldstr_checked (cfg->domain, ji->image, mono_metadata_token_index (ji->token), &cfg->error);
6833 return_val_if_nok (&cfg->error, NULL);
6834 MONO_INST_NEW (cfg, ins, OP_OBJC_GET_SELECTOR);
6835 ins->dreg = mono_alloc_ireg (cfg);
6837 ins->inst_p0 = mono_string_to_utf8_checked (s, &cfg->error);
6838 return_val_if_nok (&cfg->error, NULL);
6839 MONO_ADD_INS (cfg->cbb, ins);
6844 #ifdef MONO_ARCH_SIMD_INTRINSICS
6845 if (cfg->opt & MONO_OPT_SIMD) {
6846 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
6852 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
6856 if (COMPILE_LLVM (cfg)) {
6857 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args);
6862 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
6866 * This entry point could be used later for arbitrary method
6869 inline static MonoInst*
6870 mini_redirect_call (MonoCompile *cfg, MonoMethod *method,
6871 MonoMethodSignature *signature, MonoInst **args, MonoInst *this_ins)
6873 if (method->klass == mono_defaults.string_class) {
6874 /* managed string allocation support */
6875 if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
6876 MonoInst *iargs [2];
6877 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
6878 MonoMethod *managed_alloc = NULL;
6880 g_assert (vtable); /*Should not fail since it System.String*/
6881 #ifndef MONO_CROSS_COMPILE
6882 managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE, FALSE);
6886 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
6887 iargs [1] = args [0];
6888 return mono_emit_method_call (cfg, managed_alloc, iargs, this_ins);
6895 mono_save_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **sp)
6897 MonoInst *store, *temp;
6900 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
6901 MonoType *argtype = (sig->hasthis && (i == 0)) ? type_from_stack_type (*sp) : sig->params [i - sig->hasthis];
6904 * FIXME: We should use *args++ = sp [0], but that would mean the arg
6905 * would be different than the MonoInst's used to represent arguments, and
6906 * the ldelema implementation can't deal with that.
6907 * Solution: When ldelema is used on an inline argument, create a var for
6908 * it, emit ldelema on that var, and emit the saving code below in
6909 * inline_method () if needed.
6911 temp = mono_compile_create_var (cfg, argtype, OP_LOCAL);
6912 cfg->args [i] = temp;
6913 /* This uses cfg->args [i] which is set by the preceeding line */
6914 EMIT_NEW_ARGSTORE (cfg, store, i, *sp);
6915 store->cil_code = sp [0]->cil_code;
6920 #define MONO_INLINE_CALLED_LIMITED_METHODS 1
6921 #define MONO_INLINE_CALLER_LIMITED_METHODS 1
6923 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6925 check_inline_called_method_name_limit (MonoMethod *called_method)
6928 static const char *limit = NULL;
6930 if (limit == NULL) {
6931 const char *limit_string = g_getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
6933 if (limit_string != NULL)
6934 limit = limit_string;
6939 if (limit [0] != '\0') {
6940 char *called_method_name = mono_method_full_name (called_method, TRUE);
6942 strncmp_result = strncmp (called_method_name, limit, strlen (limit));
6943 g_free (called_method_name);
6945 //return (strncmp_result <= 0);
6946 return (strncmp_result == 0);
6953 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6955 check_inline_caller_method_name_limit (MonoMethod *caller_method)
6958 static const char *limit = NULL;
6960 if (limit == NULL) {
6961 const char *limit_string = g_getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
6962 if (limit_string != NULL) {
6963 limit = limit_string;
6969 if (limit [0] != '\0') {
6970 char *caller_method_name = mono_method_full_name (caller_method, TRUE);
6972 strncmp_result = strncmp (caller_method_name, limit, strlen (limit));
6973 g_free (caller_method_name);
6975 //return (strncmp_result <= 0);
6976 return (strncmp_result == 0);
6984 emit_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6986 static double r8_0 = 0.0;
6987 static float r4_0 = 0.0;
6991 rtype = mini_get_underlying_type (rtype);
6995 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6996 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6997 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6998 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6999 MONO_EMIT_NEW_I8CONST (cfg, dreg, 0);
7000 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
7001 MONO_INST_NEW (cfg, ins, OP_R4CONST);
7002 ins->type = STACK_R4;
7003 ins->inst_p0 = (void*)&r4_0;
7005 MONO_ADD_INS (cfg->cbb, ins);
7006 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
7007 MONO_INST_NEW (cfg, ins, OP_R8CONST);
7008 ins->type = STACK_R8;
7009 ins->inst_p0 = (void*)&r8_0;
7011 MONO_ADD_INS (cfg->cbb, ins);
7012 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
7013 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
7014 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
7015 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (rtype)) {
7016 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
7018 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
7023 emit_dummy_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
7027 rtype = mini_get_underlying_type (rtype);
7031 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_PCONST);
7032 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
7033 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_ICONST);
7034 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
7035 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_I8CONST);
7036 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
7037 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R4CONST);
7038 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
7039 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R8CONST);
7040 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
7041 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
7042 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
7043 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (rtype)) {
7044 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
7046 emit_init_rvar (cfg, dreg, rtype);
7050 /* If INIT is FALSE, emit dummy initialization statements to keep the IR valid */
7052 emit_init_local (MonoCompile *cfg, int local, MonoType *type, gboolean init)
7054 MonoInst *var = cfg->locals [local];
7055 if (COMPILE_SOFT_FLOAT (cfg)) {
7057 int reg = alloc_dreg (cfg, (MonoStackType)var->type);
7058 emit_init_rvar (cfg, reg, type);
7059 EMIT_NEW_LOCSTORE (cfg, store, local, cfg->cbb->last_ins);
7062 emit_init_rvar (cfg, var->dreg, type);
7064 emit_dummy_init_rvar (cfg, var->dreg, type);
7071 * Return the cost of inlining CMETHOD.
7074 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
7075 guchar *ip, guint real_offset, gboolean inline_always)
7078 MonoInst *ins, *rvar = NULL;
7079 MonoMethodHeader *cheader;
7080 MonoBasicBlock *ebblock, *sbblock;
7082 MonoMethod *prev_inlined_method;
7083 MonoInst **prev_locals, **prev_args;
7084 MonoType **prev_arg_types;
7085 guint prev_real_offset;
7086 GHashTable *prev_cbb_hash;
7087 MonoBasicBlock **prev_cil_offset_to_bb;
7088 MonoBasicBlock *prev_cbb;
7089 unsigned char* prev_cil_start;
7090 guint32 prev_cil_offset_to_bb_len;
7091 MonoMethod *prev_current_method;
7092 MonoGenericContext *prev_generic_context;
7093 gboolean ret_var_set, prev_ret_var_set, prev_disable_inline, virtual_ = FALSE;
7095 g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
7097 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
7098 if ((! inline_always) && ! check_inline_called_method_name_limit (cmethod))
7101 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
7102 if ((! inline_always) && ! check_inline_caller_method_name_limit (cfg->method))
7107 fsig = mono_method_signature (cmethod);
7109 if (cfg->verbose_level > 2)
7110 printf ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
7112 if (!cmethod->inline_info) {
7113 cfg->stat_inlineable_methods++;
7114 cmethod->inline_info = 1;
7117 /* allocate local variables */
7118 cheader = mono_method_get_header_checked (cmethod, &error);
7120 if (inline_always) {
7121 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
7122 mono_error_move (&cfg->error, &error);
7124 mono_error_cleanup (&error);
7129 /*Must verify before creating locals as it can cause the JIT to assert.*/
7130 if (mono_compile_is_broken (cfg, cmethod, FALSE)) {
7131 mono_metadata_free_mh (cheader);
7135 /* allocate space to store the return value */
7136 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
7137 rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
7140 prev_locals = cfg->locals;
7141 cfg->locals = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, cheader->num_locals * sizeof (MonoInst*));
7142 for (i = 0; i < cheader->num_locals; ++i)
7143 cfg->locals [i] = mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
7145 /* allocate start and end blocks */
7146 /* This is needed so if the inline is aborted, we can clean up */
7147 NEW_BBLOCK (cfg, sbblock);
7148 sbblock->real_offset = real_offset;
7150 NEW_BBLOCK (cfg, ebblock);
7151 ebblock->block_num = cfg->num_bblocks++;
7152 ebblock->real_offset = real_offset;
7154 prev_args = cfg->args;
7155 prev_arg_types = cfg->arg_types;
7156 prev_inlined_method = cfg->inlined_method;
7157 cfg->inlined_method = cmethod;
7158 cfg->ret_var_set = FALSE;
7159 cfg->inline_depth ++;
7160 prev_real_offset = cfg->real_offset;
7161 prev_cbb_hash = cfg->cbb_hash;
7162 prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
7163 prev_cil_offset_to_bb_len = cfg->cil_offset_to_bb_len;
7164 prev_cil_start = cfg->cil_start;
7165 prev_cbb = cfg->cbb;
7166 prev_current_method = cfg->current_method;
7167 prev_generic_context = cfg->generic_context;
7168 prev_ret_var_set = cfg->ret_var_set;
7169 prev_disable_inline = cfg->disable_inline;
7171 if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
7174 costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual_);
7176 ret_var_set = cfg->ret_var_set;
7178 cfg->inlined_method = prev_inlined_method;
7179 cfg->real_offset = prev_real_offset;
7180 cfg->cbb_hash = prev_cbb_hash;
7181 cfg->cil_offset_to_bb = prev_cil_offset_to_bb;
7182 cfg->cil_offset_to_bb_len = prev_cil_offset_to_bb_len;
7183 cfg->cil_start = prev_cil_start;
7184 cfg->locals = prev_locals;
7185 cfg->args = prev_args;
7186 cfg->arg_types = prev_arg_types;
7187 cfg->current_method = prev_current_method;
7188 cfg->generic_context = prev_generic_context;
7189 cfg->ret_var_set = prev_ret_var_set;
7190 cfg->disable_inline = prev_disable_inline;
7191 cfg->inline_depth --;
7193 if ((costs >= 0 && costs < 60) || inline_always || (costs >= 0 && (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))) {
7194 if (cfg->verbose_level > 2)
7195 printf ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
7197 cfg->stat_inlined_methods++;
7199 /* always add some code to avoid block split failures */
7200 MONO_INST_NEW (cfg, ins, OP_NOP);
7201 MONO_ADD_INS (prev_cbb, ins);
7203 prev_cbb->next_bb = sbblock;
7204 link_bblock (cfg, prev_cbb, sbblock);
7207 * Get rid of the begin and end bblocks if possible to aid local
7210 mono_merge_basic_blocks (cfg, prev_cbb, sbblock);
7212 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] != ebblock))
7213 mono_merge_basic_blocks (cfg, prev_cbb, prev_cbb->out_bb [0]);
7215 if ((ebblock->in_count == 1) && ebblock->in_bb [0]->out_count == 1) {
7216 MonoBasicBlock *prev = ebblock->in_bb [0];
7218 if (prev->next_bb == ebblock) {
7219 mono_merge_basic_blocks (cfg, prev, ebblock);
7221 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] == prev)) {
7222 mono_merge_basic_blocks (cfg, prev_cbb, prev);
7223 cfg->cbb = prev_cbb;
7226 /* There could be a bblock after 'prev', and making 'prev' the current bb could cause problems */
7231 * Its possible that the rvar is set in some prev bblock, but not in others.
7237 for (i = 0; i < ebblock->in_count; ++i) {
7238 bb = ebblock->in_bb [i];
7240 if (bb->last_ins && bb->last_ins->opcode == OP_NOT_REACHED) {
7243 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
7253 * If the inlined method contains only a throw, then the ret var is not
7254 * set, so set it to a dummy value.
7257 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
7259 EMIT_NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
7262 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
7265 if (cfg->verbose_level > 2)
7266 printf ("INLINE ABORTED %s (cost %d)\n", mono_method_full_name (cmethod, TRUE), costs);
7267 cfg->exception_type = MONO_EXCEPTION_NONE;
7269 /* This gets rid of the newly added bblocks */
7270 cfg->cbb = prev_cbb;
7272 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
7277 * Some of these comments may well be out-of-date.
7278 * Design decisions: we do a single pass over the IL code (and we do bblock
7279 * splitting/merging in the few cases when it's required: a back jump to an IL
7280 * address that was not already seen as bblock starting point).
7281 * Code is validated as we go (full verification is still better left to metadata/verify.c).
7282 * Complex operations are decomposed in simpler ones right away. We need to let the
7283 * arch-specific code peek and poke inside this process somehow (except when the
7284 * optimizations can take advantage of the full semantic info of coarse opcodes).
7285 * All the opcodes of the form opcode.s are 'normalized' to opcode.
7286 * MonoInst->opcode initially is the IL opcode or some simplification of that
7287 * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific
7288 * opcode with value bigger than OP_LAST.
7289 * At this point the IR can be handed over to an interpreter, a dumb code generator
7290 * or to the optimizing code generator that will translate it to SSA form.
7292 * Profiling directed optimizations.
7293 * We may compile by default with few or no optimizations and instrument the code
7294 * or the user may indicate what methods to optimize the most either in a config file
7295 * or through repeated runs where the compiler applies offline the optimizations to
7296 * each method and then decides if it was worth it.
7299 #define CHECK_TYPE(ins) if (!(ins)->type) UNVERIFIED
7300 #define CHECK_STACK(num) if ((sp - stack_start) < (num)) UNVERIFIED
7301 #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) UNVERIFIED
7302 #define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
7303 #define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
7304 #define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
7305 #define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
7306 #define CHECK_TYPELOAD(klass) if (!(klass) || mono_class_has_failure (klass)) TYPE_LOAD_ERROR ((klass))
7308 /* offset from br.s -> br like opcodes */
7309 #define BIG_BRANCH_OFFSET 13
7312 ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip)
7314 MonoBasicBlock *b = cfg->cil_offset_to_bb [ip - cfg->cil_start];
7316 return b == NULL || b == bb;
7320 get_basic_blocks (MonoCompile *cfg, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos)
7322 unsigned char *ip = start;
7323 unsigned char *target;
7326 MonoBasicBlock *bblock;
7327 const MonoOpcode *opcode;
7330 cli_addr = ip - start;
7331 i = mono_opcode_value ((const guint8 **)&ip, end);
7334 opcode = &mono_opcodes [i];
7335 switch (opcode->argument) {
7336 case MonoInlineNone:
7339 case MonoInlineString:
7340 case MonoInlineType:
7341 case MonoInlineField:
7342 case MonoInlineMethod:
7345 case MonoShortInlineR:
7352 case MonoShortInlineVar:
7353 case MonoShortInlineI:
7356 case MonoShortInlineBrTarget:
7357 target = start + cli_addr + 2 + (signed char)ip [1];
7358 GET_BBLOCK (cfg, bblock, target);
7361 GET_BBLOCK (cfg, bblock, ip);
7363 case MonoInlineBrTarget:
7364 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
7365 GET_BBLOCK (cfg, bblock, target);
7368 GET_BBLOCK (cfg, bblock, ip);
7370 case MonoInlineSwitch: {
7371 guint32 n = read32 (ip + 1);
7374 cli_addr += 5 + 4 * n;
7375 target = start + cli_addr;
7376 GET_BBLOCK (cfg, bblock, target);
7378 for (j = 0; j < n; ++j) {
7379 target = start + cli_addr + (gint32)read32 (ip);
7380 GET_BBLOCK (cfg, bblock, target);
7390 g_assert_not_reached ();
7393 if (i == CEE_THROW) {
7394 unsigned char *bb_start = ip - 1;
7396 /* Find the start of the bblock containing the throw */
7398 while ((bb_start >= start) && !bblock) {
7399 bblock = cfg->cil_offset_to_bb [(bb_start) - start];
7403 bblock->out_of_line = 1;
7413 static inline MonoMethod *
7414 mini_get_method_allow_open (MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context, MonoError *error)
7418 mono_error_init (error);
7420 if (m->wrapper_type != MONO_WRAPPER_NONE) {
7421 method = (MonoMethod *)mono_method_get_wrapper_data (m, token);
7423 method = mono_class_inflate_generic_method_checked (method, context, error);
7426 method = mono_get_method_checked (m->klass->image, token, klass, context, error);
7432 static inline MonoMethod *
7433 mini_get_method (MonoCompile *cfg, MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
7436 MonoMethod *method = mini_get_method_allow_open (m, token, klass, context, cfg ? &cfg->error : &error);
7438 if (method && cfg && !cfg->gshared && mono_class_is_open_constructed_type (&method->klass->byval_arg)) {
7439 mono_error_set_bad_image (&cfg->error, cfg->method->klass->image, "Method with open type while not compiling gshared");
7443 if (!method && !cfg)
7444 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7449 static inline MonoClass*
7450 mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
7455 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7456 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
7458 klass = mono_class_inflate_generic_class_checked (klass, context, &error);
7459 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7462 klass = mono_class_get_and_inflate_typespec_checked (method->klass->image, token, context, &error);
7463 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7466 mono_class_init (klass);
7470 static inline MonoMethodSignature*
7471 mini_get_signature (MonoMethod *method, guint32 token, MonoGenericContext *context, MonoError *error)
7473 MonoMethodSignature *fsig;
7475 mono_error_init (error);
7476 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7477 fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
7479 fsig = mono_metadata_parse_signature_checked (method->klass->image, token, error);
7480 return_val_if_nok (error, NULL);
7483 fsig = mono_inflate_generic_signature(fsig, context, error);
7489 throw_exception (void)
7491 static MonoMethod *method = NULL;
7494 MonoSecurityManager *secman = mono_security_manager_get_methods ();
7495 method = mono_class_get_method_from_name (secman->securitymanager, "ThrowException", 1);
7502 emit_throw_exception (MonoCompile *cfg, MonoException *ex)
7504 MonoMethod *thrower = throw_exception ();
7507 EMIT_NEW_PCONST (cfg, args [0], ex);
7508 mono_emit_method_call (cfg, thrower, args, NULL);
7512 * Return the original method is a wrapper is specified. We can only access
7513 * the custom attributes from the original method.
7516 get_original_method (MonoMethod *method)
7518 if (method->wrapper_type == MONO_WRAPPER_NONE)
7521 /* native code (which is like Critical) can call any managed method XXX FIXME XXX to validate all usages */
7522 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
7525 /* in other cases we need to find the original method */
7526 return mono_marshal_method_from_wrapper (method);
7530 ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field)
7532 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7533 MonoException *ex = mono_security_core_clr_is_field_access_allowed (get_original_method (caller), field);
7535 emit_throw_exception (cfg, ex);
7539 ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee)
7541 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7542 MonoException *ex = mono_security_core_clr_is_call_allowed (get_original_method (caller), callee);
7544 emit_throw_exception (cfg, ex);
7548 * Check that the IL instructions at ip are the array initialization
7549 * sequence and return the pointer to the data and the size.
7552 initialize_array_data (MonoMethod *method, gboolean aot, unsigned char *ip, MonoClass *klass, guint32 len, int *out_size, guint32 *out_field_token)
7555 * newarr[System.Int32]
7557 * ldtoken field valuetype ...
7558 * call void class [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
7560 if (ip [0] == CEE_DUP && ip [1] == CEE_LDTOKEN && ip [5] == 0x4 && ip [6] == CEE_CALL) {
7562 guint32 token = read32 (ip + 7);
7563 guint32 field_token = read32 (ip + 2);
7564 guint32 field_index = field_token & 0xffffff;
7566 const char *data_ptr;
7568 MonoMethod *cmethod;
7569 MonoClass *dummy_class;
7570 MonoClassField *field = mono_field_from_token_checked (method->klass->image, field_token, &dummy_class, NULL, &error);
7574 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7578 *out_field_token = field_token;
7580 cmethod = mini_get_method (NULL, method, token, NULL, NULL);
7583 if (strcmp (cmethod->name, "InitializeArray") || strcmp (cmethod->klass->name, "RuntimeHelpers") || cmethod->klass->image != mono_defaults.corlib)
7585 switch (mono_type_get_underlying_type (&klass->byval_arg)->type) {
7586 case MONO_TYPE_BOOLEAN:
7590 /* we need to swap on big endian, so punt. Should we handle R4 and R8 as well? */
7591 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
7592 case MONO_TYPE_CHAR:
7609 if (size > mono_type_size (field->type, &dummy_align))
7612 /*g_print ("optimized in %s: size: %d, numelems: %d\n", method->name, size, newarr->inst_newa_len->inst_c0);*/
7613 if (!image_is_dynamic (method->klass->image)) {
7614 field_index = read32 (ip + 2) & 0xffffff;
7615 mono_metadata_field_info (method->klass->image, field_index - 1, NULL, &rva, NULL);
7616 data_ptr = mono_image_rva_map (method->klass->image, rva);
7617 /*g_print ("field: 0x%08x, rva: %d, rva_ptr: %p\n", read32 (ip + 2), rva, data_ptr);*/
7618 /* for aot code we do the lookup on load */
7619 if (aot && data_ptr)
7620 return (const char *)GUINT_TO_POINTER (rva);
7622 /*FIXME is it possible to AOT a SRE assembly not meant to be saved? */
7624 data_ptr = mono_field_get_data (field);
7632 set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsigned char *ip)
7635 char *method_fname = mono_method_full_name (method, TRUE);
7637 MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
7640 method_code = g_strdup_printf ("could not parse method body due to %s", mono_error_get_message (&error));
7641 mono_error_cleanup (&error);
7642 } else if (header->code_size == 0)
7643 method_code = g_strdup ("method body is empty.");
7645 method_code = mono_disasm_code_one (NULL, method, ip, NULL);
7646 mono_cfg_set_exception_invalid_program (cfg, g_strdup_printf ("Invalid IL code in %s: %s\n", method_fname, method_code));
7647 g_free (method_fname);
7648 g_free (method_code);
7649 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
7653 emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
7656 guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
7657 if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] &&
7658 ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
7659 /* Optimize reg-reg moves away */
7661 * Can't optimize other opcodes, since sp[0] might point to
7662 * the last ins of a decomposed opcode.
7664 sp [0]->dreg = (cfg)->locals [n]->dreg;
7666 EMIT_NEW_LOCSTORE (cfg, ins, n, *sp);
7671 * ldloca inhibits many optimizations so try to get rid of it in common
7674 static inline unsigned char *
7675 emit_optimized_ldloca_ir (MonoCompile *cfg, unsigned char *ip, unsigned char *end, int size)
7685 local = read16 (ip + 2);
7689 if (ip + 6 < end && (ip [0] == CEE_PREFIX1) && (ip [1] == CEE_INITOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 1)) {
7690 /* From the INITOBJ case */
7691 token = read32 (ip + 2);
7692 klass = mini_get_class (cfg->current_method, token, cfg->generic_context);
7693 CHECK_TYPELOAD (klass);
7694 type = mini_get_underlying_type (&klass->byval_arg);
7695 emit_init_local (cfg, local, type, TRUE);
7703 emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp)
7705 MonoInst *icall_args [16];
7706 MonoInst *call_target, *ins, *vtable_ins;
7707 int arg_reg, this_reg, vtable_reg;
7708 gboolean is_iface = cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE;
7709 gboolean is_gsharedvt = cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig);
7710 gboolean variant_iface = FALSE;
7715 * In llvm-only mode, vtables contain function descriptors instead of
7716 * method addresses/trampolines.
7718 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
7721 slot = mono_method_get_imt_slot (cmethod);
7723 slot = mono_method_get_vtable_index (cmethod);
7725 this_reg = sp [0]->dreg;
7727 if (is_iface && mono_class_has_variant_generic_params (cmethod->klass))
7728 variant_iface = TRUE;
7730 if (!fsig->generic_param_count && !is_iface && !is_gsharedvt) {
7732 * The simplest case, a normal virtual call.
7734 int slot_reg = alloc_preg (cfg);
7735 int addr_reg = alloc_preg (cfg);
7736 int arg_reg = alloc_preg (cfg);
7737 MonoBasicBlock *non_null_bb;
7739 vtable_reg = alloc_preg (cfg);
7740 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
7741 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) + (slot * SIZEOF_VOID_P);
7743 /* Load the vtable slot, which contains a function descriptor. */
7744 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
7746 NEW_BBLOCK (cfg, non_null_bb);
7748 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_reg, 0);
7749 cfg->cbb->last_ins->flags |= MONO_INST_LIKELY;
7750 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, non_null_bb);
7753 // FIXME: Make the wrapper use the preserveall cconv
7754 // FIXME: Use one icall per slot for small slot numbers ?
7755 icall_args [0] = vtable_ins;
7756 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
7757 /* Make the icall return the vtable slot value to save some code space */
7758 ins = mono_emit_jit_icall (cfg, mono_init_vtable_slot, icall_args);
7759 ins->dreg = slot_reg;
7760 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, non_null_bb);
7763 MONO_START_BB (cfg, non_null_bb);
7764 /* Load the address + arg from the vtable slot */
7765 EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
7766 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, slot_reg, SIZEOF_VOID_P);
7768 return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
7771 if (!fsig->generic_param_count && is_iface && !variant_iface && !is_gsharedvt) {
7773 * A simple interface call
7775 * We make a call through an imt slot to obtain the function descriptor we need to call.
7776 * The imt slot contains a function descriptor for a runtime function + arg.
7778 int slot_reg = alloc_preg (cfg);
7779 int addr_reg = alloc_preg (cfg);
7780 int arg_reg = alloc_preg (cfg);
7781 MonoInst *thunk_addr_ins, *thunk_arg_ins, *ftndesc_ins;
7783 vtable_reg = alloc_preg (cfg);
7784 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
7785 offset = ((gint32)slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
7788 * The slot is already initialized when the vtable is created so there is no need
7792 /* Load the imt slot, which contains a function descriptor. */
7793 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
7795 /* Load the address + arg of the imt thunk from the imt slot */
7796 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_addr_ins, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
7797 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_arg_ins, OP_LOAD_MEMBASE, arg_reg, slot_reg, SIZEOF_VOID_P);
7799 * IMT thunks in llvm-only mode are C functions which take an info argument
7800 * plus the imt method and return the ftndesc to call.
7802 icall_args [0] = thunk_arg_ins;
7803 icall_args [1] = emit_get_rgctx_method (cfg, context_used,
7804 cmethod, MONO_RGCTX_INFO_METHOD);
7805 ftndesc_ins = mono_emit_calli (cfg, helper_sig_llvmonly_imt_thunk, icall_args, thunk_addr_ins, NULL, NULL);
7807 return emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins);
7810 if ((fsig->generic_param_count || variant_iface) && !is_gsharedvt) {
7812 * This is similar to the interface case, the vtable slot points to an imt thunk which is
7813 * dynamically extended as more instantiations are discovered.
7814 * This handles generic virtual methods both on classes and interfaces.
7816 int slot_reg = alloc_preg (cfg);
7817 int addr_reg = alloc_preg (cfg);
7818 int arg_reg = alloc_preg (cfg);
7819 int ftndesc_reg = alloc_preg (cfg);
7820 MonoInst *thunk_addr_ins, *thunk_arg_ins, *ftndesc_ins;
7821 MonoBasicBlock *slowpath_bb, *end_bb;
7823 NEW_BBLOCK (cfg, slowpath_bb);
7824 NEW_BBLOCK (cfg, end_bb);
7826 vtable_reg = alloc_preg (cfg);
7827 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
7829 offset = ((gint32)slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
7831 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) + (slot * SIZEOF_VOID_P);
7833 /* Load the slot, which contains a function descriptor. */
7834 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
7836 /* These slots are not initialized, so fall back to the slow path until they are initialized */
7837 /* That happens when mono_method_add_generic_virtual_invocation () creates an IMT thunk */
7838 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_reg, 0);
7839 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb);
7842 /* Same as with iface calls */
7843 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_addr_ins, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
7844 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_arg_ins, OP_LOAD_MEMBASE, arg_reg, slot_reg, SIZEOF_VOID_P);
7845 icall_args [0] = thunk_arg_ins;
7846 icall_args [1] = emit_get_rgctx_method (cfg, context_used,
7847 cmethod, MONO_RGCTX_INFO_METHOD);
7848 ftndesc_ins = mono_emit_calli (cfg, helper_sig_llvmonly_imt_thunk, icall_args, thunk_addr_ins, NULL, NULL);
7849 ftndesc_ins->dreg = ftndesc_reg;
7851 * Unlike normal iface calls, these imt thunks can return NULL, i.e. when they are passed an instantiation
7852 * they don't know about yet. Fall back to the slowpath in that case.
7854 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, ftndesc_reg, 0);
7855 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb);
7857 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
7860 MONO_START_BB (cfg, slowpath_bb);
7861 icall_args [0] = vtable_ins;
7862 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
7863 icall_args [2] = emit_get_rgctx_method (cfg, context_used,
7864 cmethod, MONO_RGCTX_INFO_METHOD);
7866 ftndesc_ins = mono_emit_jit_icall (cfg, mono_resolve_generic_virtual_iface_call, icall_args);
7868 ftndesc_ins = mono_emit_jit_icall (cfg, mono_resolve_generic_virtual_call, icall_args);
7869 ftndesc_ins->dreg = ftndesc_reg;
7870 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
7873 MONO_START_BB (cfg, end_bb);
7874 return emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins);
7878 * Non-optimized cases
7880 icall_args [0] = sp [0];
7881 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
7883 icall_args [2] = emit_get_rgctx_method (cfg, context_used,
7884 cmethod, MONO_RGCTX_INFO_METHOD);
7886 arg_reg = alloc_preg (cfg);
7887 MONO_EMIT_NEW_PCONST (cfg, arg_reg, NULL);
7888 EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], arg_reg, &mono_defaults.int_class->byval_arg);
7890 g_assert (is_gsharedvt);
7892 call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call_gsharedvt, icall_args);
7894 call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall_gsharedvt, icall_args);
7897 * Pass the extra argument even if the callee doesn't receive it, most
7898 * calling conventions allow this.
7900 return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
7904 is_exception_class (MonoClass *klass)
7907 if (klass == mono_defaults.exception_class)
7909 klass = klass->parent;
7915 * is_jit_optimizer_disabled:
7917 * Determine whenever M's assembly has a DebuggableAttribute with the
7918 * IsJITOptimizerDisabled flag set.
7921 is_jit_optimizer_disabled (MonoMethod *m)
7924 MonoAssembly *ass = m->klass->image->assembly;
7925 MonoCustomAttrInfo* attrs;
7928 gboolean val = FALSE;
7931 if (ass->jit_optimizer_disabled_inited)
7932 return ass->jit_optimizer_disabled;
7934 klass = mono_class_try_get_debuggable_attribute_class ();
7938 ass->jit_optimizer_disabled = FALSE;
7939 mono_memory_barrier ();
7940 ass->jit_optimizer_disabled_inited = TRUE;
7944 attrs = mono_custom_attrs_from_assembly_checked (ass, &error);
7945 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7947 for (i = 0; i < attrs->num_attrs; ++i) {
7948 MonoCustomAttrEntry *attr = &attrs->attrs [i];
7950 MonoMethodSignature *sig;
7952 if (!attr->ctor || attr->ctor->klass != klass)
7954 /* Decode the attribute. See reflection.c */
7955 p = (const char*)attr->data;
7956 g_assert (read16 (p) == 0x0001);
7959 // FIXME: Support named parameters
7960 sig = mono_method_signature (attr->ctor);
7961 if (sig->param_count != 2 || sig->params [0]->type != MONO_TYPE_BOOLEAN || sig->params [1]->type != MONO_TYPE_BOOLEAN)
7963 /* Two boolean arguments */
7967 mono_custom_attrs_free (attrs);
7970 ass->jit_optimizer_disabled = val;
7971 mono_memory_barrier ();
7972 ass->jit_optimizer_disabled_inited = TRUE;
7978 is_supported_tail_call (MonoCompile *cfg, MonoMethod *method, MonoMethod *cmethod, MonoMethodSignature *fsig, int call_opcode)
7980 gboolean supported_tail_call;
7983 supported_tail_call = mono_arch_tail_call_supported (cfg, mono_method_signature (method), mono_method_signature (cmethod));
7985 for (i = 0; i < fsig->param_count; ++i) {
7986 if (fsig->params [i]->byref || fsig->params [i]->type == MONO_TYPE_PTR || fsig->params [i]->type == MONO_TYPE_FNPTR)
7987 /* These can point to the current method's stack */
7988 supported_tail_call = FALSE;
7990 if (fsig->hasthis && cmethod->klass->valuetype)
7991 /* this might point to the current method's stack */
7992 supported_tail_call = FALSE;
7993 if (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
7994 supported_tail_call = FALSE;
7995 if (cfg->method->save_lmf)
7996 supported_tail_call = FALSE;
7997 if (cmethod->wrapper_type && cmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
7998 supported_tail_call = FALSE;
7999 if (call_opcode != CEE_CALL)
8000 supported_tail_call = FALSE;
8002 /* Debugging support */
8004 if (supported_tail_call) {
8005 if (!mono_debug_count ())
8006 supported_tail_call = FALSE;
8010 return supported_tail_call;
8016 * Handle calls made to ctors from NEWOBJ opcodes.
8019 handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used,
8020 MonoInst **sp, guint8 *ip, int *inline_costs)
8022 MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins;
8024 if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
8025 mono_method_is_generic_sharable (cmethod, TRUE)) {
8026 if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
8027 mono_class_vtable (cfg->domain, cmethod->klass);
8028 CHECK_TYPELOAD (cmethod->klass);
8030 vtable_arg = emit_get_rgctx_method (cfg, context_used,
8031 cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
8034 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
8035 cmethod->klass, MONO_RGCTX_INFO_VTABLE);
8037 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
8039 CHECK_TYPELOAD (cmethod->klass);
8040 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
8045 /* Avoid virtual calls to ctors if possible */
8046 if (mono_class_is_marshalbyref (cmethod->klass))
8047 callvirt_this_arg = sp [0];
8049 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
8050 g_assert (MONO_TYPE_IS_VOID (fsig->ret));
8051 CHECK_CFG_EXCEPTION;
8052 } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
8053 mono_method_check_inlining (cfg, cmethod) &&
8054 !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE)) {
8057 if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, FALSE))) {
8058 cfg->real_offset += 5;
8060 *inline_costs += costs - 5;
8062 INLINE_FAILURE ("inline failure");
8063 // FIXME-VT: Clean this up
8064 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig))
8065 GSHAREDVT_FAILURE(*ip);
8066 mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
8068 } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
8071 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
8073 if (cfg->llvm_only) {
8074 // FIXME: Avoid initializing vtable_arg
8075 emit_llvmonly_calli (cfg, fsig, sp, addr);
8077 mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
8079 } else if (context_used &&
8080 ((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
8081 !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
8082 MonoInst *cmethod_addr;
8084 /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
8086 if (cfg->llvm_only) {
8087 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, cmethod,
8088 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
8089 emit_llvmonly_calli (cfg, fsig, sp, addr);
8091 cmethod_addr = emit_get_rgctx_method (cfg, context_used,
8092 cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
8094 mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
8097 INLINE_FAILURE ("ctor call");
8098 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
8099 callvirt_this_arg, NULL, vtable_arg);
8106 emit_setret (MonoCompile *cfg, MonoInst *val)
8108 MonoType *ret_type = mini_get_underlying_type (mono_method_signature (cfg->method)->ret);
8111 if (mini_type_to_stind (cfg, ret_type) == CEE_STOBJ) {
8114 if (!cfg->vret_addr) {
8115 EMIT_NEW_VARSTORE (cfg, ins, cfg->ret, ret_type, val);
8117 EMIT_NEW_RETLOADA (cfg, ret_addr);
8119 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STOREV_MEMBASE, ret_addr->dreg, 0, val->dreg);
8120 ins->klass = mono_class_from_mono_type (ret_type);
8123 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
8124 if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) {
8125 MonoInst *iargs [1];
8129 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
8130 mono_arch_emit_setret (cfg, cfg->method, conv);
8132 mono_arch_emit_setret (cfg, cfg->method, val);
8135 mono_arch_emit_setret (cfg, cfg->method, val);
8141 * mono_method_to_ir:
8143 * Translate the .net IL into linear IR.
8146 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
8147 MonoInst *return_var, MonoInst **inline_args,
8148 guint inline_offset, gboolean is_virtual_call)
8151 MonoInst *ins, **sp, **stack_start;
8152 MonoBasicBlock *tblock = NULL, *init_localsbb = NULL;
8153 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
8154 MonoMethod *cmethod, *method_definition;
8155 MonoInst **arg_array;
8156 MonoMethodHeader *header;
8158 guint32 token, ins_flag;
8160 MonoClass *constrained_class = NULL;
8161 unsigned char *ip, *end, *target, *err_pos;
8162 MonoMethodSignature *sig;
8163 MonoGenericContext *generic_context = NULL;
8164 MonoGenericContainer *generic_container = NULL;
8165 MonoType **param_types;
8166 int i, n, start_new_bblock, dreg;
8167 int num_calls = 0, inline_costs = 0;
8168 int breakpoint_id = 0;
8170 GSList *class_inits = NULL;
8171 gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
8173 gboolean init_locals, seq_points, skip_dead_blocks;
8174 gboolean sym_seq_points = FALSE;
8175 MonoDebugMethodInfo *minfo;
8176 MonoBitSet *seq_point_locs = NULL;
8177 MonoBitSet *seq_point_set_locs = NULL;
8179 cfg->disable_inline = is_jit_optimizer_disabled (method);
8181 /* serialization and xdomain stuff may need access to private fields and methods */
8182 dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
8183 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
8184 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
8185 dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
8186 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
8187 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP_INVOKE;
8189 /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
8190 dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
8191 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
8192 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
8193 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_STELEMREF;
8195 image = method->klass->image;
8196 header = mono_method_get_header_checked (method, &cfg->error);
8198 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
8199 goto exception_exit;
8201 generic_container = mono_method_get_generic_container (method);
8202 sig = mono_method_signature (method);
8203 num_args = sig->hasthis + sig->param_count;
8204 ip = (unsigned char*)header->code;
8205 cfg->cil_start = ip;
8206 end = ip + header->code_size;
8207 cfg->stat_cil_code_size += header->code_size;
8209 seq_points = cfg->gen_seq_points && cfg->method == method;
8211 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
8212 /* We could hit a seq point before attaching to the JIT (#8338) */
8216 if (cfg->gen_sdb_seq_points && cfg->method == method) {
8217 minfo = mono_debug_lookup_method (method);
8219 MonoSymSeqPoint *sps;
8220 int i, n_il_offsets;
8222 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
8223 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
8224 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);
8225 sym_seq_points = TRUE;
8226 for (i = 0; i < n_il_offsets; ++i) {
8227 if (sps [i].il_offset < header->code_size)
8228 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
8231 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) {
8232 /* Methods without line number info like auto-generated property accessors */
8233 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
8234 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);
8235 sym_seq_points = TRUE;
8240 * Methods without init_locals set could cause asserts in various passes
8241 * (#497220). To work around this, we emit dummy initialization opcodes
8242 * (OP_DUMMY_ICONST etc.) which generate no code. These are only supported
8243 * on some platforms.
8245 if ((cfg->opt & MONO_OPT_UNSAFE) && cfg->backend->have_dummy_init)
8246 init_locals = header->init_locals;
8250 method_definition = method;
8251 while (method_definition->is_inflated) {
8252 MonoMethodInflated *imethod = (MonoMethodInflated *) method_definition;
8253 method_definition = imethod->declaring;
8256 /* SkipVerification is not allowed if core-clr is enabled */
8257 if (!dont_verify && mini_assembly_can_skip_verification (cfg->domain, method)) {
8259 dont_verify_stloc = TRUE;
8262 if (sig->is_inflated)
8263 generic_context = mono_method_get_context (method);
8264 else if (generic_container)
8265 generic_context = &generic_container->context;
8266 cfg->generic_context = generic_context;
8269 g_assert (!sig->has_type_parameters);
8271 if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
8272 g_assert (method->is_inflated);
8273 g_assert (mono_method_get_context (method)->method_inst);
8275 if (method->is_inflated && mono_method_get_context (method)->method_inst)
8276 g_assert (sig->generic_param_count);
8278 if (cfg->method == method) {
8279 cfg->real_offset = 0;
8281 cfg->real_offset = inline_offset;
8284 cfg->cil_offset_to_bb = (MonoBasicBlock **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * header->code_size);
8285 cfg->cil_offset_to_bb_len = header->code_size;
8287 cfg->current_method = method;
8289 if (cfg->verbose_level > 2)
8290 printf ("method to IR %s\n", mono_method_full_name (method, TRUE));
8292 param_types = (MonoType **)mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args);
8294 param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
8295 for (n = 0; n < sig->param_count; ++n)
8296 param_types [n + sig->hasthis] = sig->params [n];
8297 cfg->arg_types = param_types;
8299 cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
8300 if (cfg->method == method) {
8302 if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
8303 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
8306 NEW_BBLOCK (cfg, start_bblock);
8307 cfg->bb_entry = start_bblock;
8308 start_bblock->cil_code = NULL;
8309 start_bblock->cil_length = 0;
8312 NEW_BBLOCK (cfg, end_bblock);
8313 cfg->bb_exit = end_bblock;
8314 end_bblock->cil_code = NULL;
8315 end_bblock->cil_length = 0;
8316 end_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
8317 g_assert (cfg->num_bblocks == 2);
8319 arg_array = cfg->args;
8321 if (header->num_clauses) {
8322 cfg->spvars = g_hash_table_new (NULL, NULL);
8323 cfg->exvars = g_hash_table_new (NULL, NULL);
8325 /* handle exception clauses */
8326 for (i = 0; i < header->num_clauses; ++i) {
8327 MonoBasicBlock *try_bb;
8328 MonoExceptionClause *clause = &header->clauses [i];
8329 GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
8331 try_bb->real_offset = clause->try_offset;
8332 try_bb->try_start = TRUE;
8333 try_bb->region = ((i + 1) << 8) | clause->flags;
8334 GET_BBLOCK (cfg, tblock, ip + clause->handler_offset);
8335 tblock->real_offset = clause->handler_offset;
8336 tblock->flags |= BB_EXCEPTION_HANDLER;
8339 * Linking the try block with the EH block hinders inlining as we won't be able to
8340 * merge the bblocks from inlining and produce an artificial hole for no good reason.
8342 if (COMPILE_LLVM (cfg))
8343 link_bblock (cfg, try_bb, tblock);
8345 if (*(ip + clause->handler_offset) == CEE_POP)
8346 tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
8348 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
8349 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER ||
8350 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
8351 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
8352 MONO_ADD_INS (tblock, ins);
8354 if (seq_points && clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_FILTER) {
8355 /* finally clauses already have a seq point */
8356 /* seq points for filter clauses are emitted below */
8357 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
8358 MONO_ADD_INS (tblock, ins);
8361 /* todo: is a fault block unsafe to optimize? */
8362 if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
8363 tblock->flags |= BB_EXCEPTION_UNSAFE;
8366 /*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);
8368 printf ("%s", mono_disasm_code_one (NULL, method, p, &p));
8370 /* catch and filter blocks get the exception object on the stack */
8371 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE ||
8372 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8374 /* mostly like handle_stack_args (), but just sets the input args */
8375 /* printf ("handling clause at IL_%04x\n", clause->handler_offset); */
8376 tblock->in_scount = 1;
8377 tblock->in_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
8378 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
8382 #ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
8383 /* The EH code passes in the exception in a register to both JITted and LLVM compiled code */
8384 if (!cfg->compile_llvm) {
8385 MONO_INST_NEW (cfg, ins, OP_GET_EX_OBJ);
8386 ins->dreg = tblock->in_stack [0]->dreg;
8387 MONO_ADD_INS (tblock, ins);
8390 MonoInst *dummy_use;
8393 * Add a dummy use for the exvar so its liveness info will be
8396 EMIT_NEW_DUMMY_USE (cfg, dummy_use, tblock->in_stack [0]);
8399 if (seq_points && clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8400 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
8401 MONO_ADD_INS (tblock, ins);
8404 if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8405 GET_BBLOCK (cfg, tblock, ip + clause->data.filter_offset);
8406 tblock->flags |= BB_EXCEPTION_HANDLER;
8407 tblock->real_offset = clause->data.filter_offset;
8408 tblock->in_scount = 1;
8409 tblock->in_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
8410 /* The filter block shares the exvar with the handler block */
8411 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
8412 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
8413 MONO_ADD_INS (tblock, ins);
8417 if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
8418 clause->data.catch_class &&
8420 mono_class_check_context_used (clause->data.catch_class)) {
8422 * In shared generic code with catch
8423 * clauses containing type variables
8424 * the exception handling code has to
8425 * be able to get to the rgctx.
8426 * Therefore we have to make sure that
8427 * the vtable/mrgctx argument (for
8428 * static or generic methods) or the
8429 * "this" argument (for non-static
8430 * methods) are live.
8432 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8433 mini_method_get_context (method)->method_inst ||
8434 method->klass->valuetype) {
8435 mono_get_vtable_var (cfg);
8437 MonoInst *dummy_use;
8439 EMIT_NEW_DUMMY_USE (cfg, dummy_use, arg_array [0]);
8444 arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args);
8445 cfg->cbb = start_bblock;
8446 cfg->args = arg_array;
8447 mono_save_args (cfg, sig, inline_args);
8450 /* FIRST CODE BLOCK */
8451 NEW_BBLOCK (cfg, tblock);
8452 tblock->cil_code = ip;
8456 ADD_BBLOCK (cfg, tblock);
8458 if (cfg->method == method) {
8459 breakpoint_id = mono_debugger_method_has_breakpoint (method);
8460 if (breakpoint_id) {
8461 MONO_INST_NEW (cfg, ins, OP_BREAK);
8462 MONO_ADD_INS (cfg->cbb, ins);
8466 /* we use a separate basic block for the initialization code */
8467 NEW_BBLOCK (cfg, init_localsbb);
8468 cfg->bb_init = init_localsbb;
8469 init_localsbb->real_offset = cfg->real_offset;
8470 start_bblock->next_bb = init_localsbb;
8471 init_localsbb->next_bb = cfg->cbb;
8472 link_bblock (cfg, start_bblock, init_localsbb);
8473 link_bblock (cfg, init_localsbb, cfg->cbb);
8475 cfg->cbb = init_localsbb;
8477 if (cfg->gsharedvt && cfg->method == method) {
8478 MonoGSharedVtMethodInfo *info;
8479 MonoInst *var, *locals_var;
8482 info = (MonoGSharedVtMethodInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGSharedVtMethodInfo));
8483 info->method = cfg->method;
8484 info->count_entries = 16;
8485 info->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
8486 cfg->gsharedvt_info = info;
8488 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8489 /* prevent it from being register allocated */
8490 //var->flags |= MONO_INST_VOLATILE;
8491 cfg->gsharedvt_info_var = var;
8493 ins = emit_get_rgctx_gsharedvt_method (cfg, mini_method_check_context_used (cfg, method), method, info);
8494 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, var->dreg, ins->dreg);
8496 /* Allocate locals */
8497 locals_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8498 /* prevent it from being register allocated */
8499 //locals_var->flags |= MONO_INST_VOLATILE;
8500 cfg->gsharedvt_locals_var = locals_var;
8502 dreg = alloc_ireg (cfg);
8503 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, dreg, var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, locals_size));
8505 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
8506 ins->dreg = locals_var->dreg;
8508 MONO_ADD_INS (cfg->cbb, ins);
8509 cfg->gsharedvt_locals_var_ins = ins;
8511 cfg->flags |= MONO_CFG_HAS_ALLOCA;
8514 ins->flags |= MONO_INST_INIT;
8518 if (mono_security_core_clr_enabled ()) {
8519 /* check if this is native code, e.g. an icall or a p/invoke */
8520 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
8521 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
8523 gboolean pinvk = (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
8524 gboolean icall = (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL);
8526 /* if this ia a native call then it can only be JITted from platform code */
8527 if ((icall || pinvk) && method->klass && method->klass->image) {
8528 if (!mono_security_core_clr_is_platform_image (method->klass->image)) {
8529 MonoException *ex = icall ? mono_get_exception_security () :
8530 mono_get_exception_method_access ();
8531 emit_throw_exception (cfg, ex);
8538 CHECK_CFG_EXCEPTION;
8540 if (header->code_size == 0)
8543 if (get_basic_blocks (cfg, header, cfg->real_offset, ip, end, &err_pos)) {
8548 if (cfg->method == method)
8549 mono_debug_init_method (cfg, cfg->cbb, breakpoint_id);
8551 for (n = 0; n < header->num_locals; ++n) {
8552 if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
8557 /* We force the vtable variable here for all shared methods
8558 for the possibility that they might show up in a stack
8559 trace where their exact instantiation is needed. */
8560 if (cfg->gshared && method == cfg->method) {
8561 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8562 mini_method_get_context (method)->method_inst ||
8563 method->klass->valuetype) {
8564 mono_get_vtable_var (cfg);
8566 /* FIXME: Is there a better way to do this?
8567 We need the variable live for the duration
8568 of the whole method. */
8569 cfg->args [0]->flags |= MONO_INST_VOLATILE;
8573 /* add a check for this != NULL to inlined methods */
8574 if (is_virtual_call) {
8577 NEW_ARGLOAD (cfg, arg_ins, 0);
8578 MONO_ADD_INS (cfg->cbb, arg_ins);
8579 MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
8582 skip_dead_blocks = !dont_verify;
8583 if (skip_dead_blocks) {
8584 original_bb = bb = mono_basic_block_split (method, &cfg->error, header);
8589 /* we use a spare stack slot in SWITCH and NEWOBJ and others */
8590 stack_start = sp = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
8593 start_new_bblock = 0;
8595 if (cfg->method == method)
8596 cfg->real_offset = ip - header->code;
8598 cfg->real_offset = inline_offset;
8603 if (start_new_bblock) {
8604 cfg->cbb->cil_length = ip - cfg->cbb->cil_code;
8605 if (start_new_bblock == 2) {
8606 g_assert (ip == tblock->cil_code);
8608 GET_BBLOCK (cfg, tblock, ip);
8610 cfg->cbb->next_bb = tblock;
8612 start_new_bblock = 0;
8613 for (i = 0; i < cfg->cbb->in_scount; ++i) {
8614 if (cfg->verbose_level > 3)
8615 printf ("loading %d from temp %d\n", i, (int)cfg->cbb->in_stack [i]->inst_c0);
8616 EMIT_NEW_TEMPLOAD (cfg, ins, cfg->cbb->in_stack [i]->inst_c0);
8620 g_slist_free (class_inits);
8623 if ((tblock = cfg->cil_offset_to_bb [ip - cfg->cil_start]) && (tblock != cfg->cbb)) {
8624 link_bblock (cfg, cfg->cbb, tblock);
8625 if (sp != stack_start) {
8626 handle_stack_args (cfg, stack_start, sp - stack_start);
8628 CHECK_UNVERIFIABLE (cfg);
8630 cfg->cbb->next_bb = tblock;
8632 for (i = 0; i < cfg->cbb->in_scount; ++i) {
8633 if (cfg->verbose_level > 3)
8634 printf ("loading %d from temp %d\n", i, (int)cfg->cbb->in_stack [i]->inst_c0);
8635 EMIT_NEW_TEMPLOAD (cfg, ins, cfg->cbb->in_stack [i]->inst_c0);
8638 g_slist_free (class_inits);
8643 if (skip_dead_blocks) {
8644 int ip_offset = ip - header->code;
8646 if (ip_offset == bb->end)
8650 int op_size = mono_opcode_size (ip, end);
8651 g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/
8653 if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset);
8655 if (ip_offset + op_size == bb->end) {
8656 MONO_INST_NEW (cfg, ins, OP_NOP);
8657 MONO_ADD_INS (cfg->cbb, ins);
8658 start_new_bblock = 1;
8666 * Sequence points are points where the debugger can place a breakpoint.
8667 * Currently, we generate these automatically at points where the IL
8670 if (seq_points && ((!sym_seq_points && (sp == stack_start)) || (sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code)))) {
8672 * Make methods interruptable at the beginning, and at the targets of
8673 * backward branches.
8674 * Also, do this at the start of every bblock in methods with clauses too,
8675 * to be able to handle instructions with inprecise control flow like
8677 * Backward branches are handled at the end of method-to-ir ().
8679 gboolean intr_loc = ip == header->code || (!cfg->cbb->last_ins && cfg->header->num_clauses);
8680 gboolean sym_seq_point = sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code);
8682 /* Avoid sequence points on empty IL like .volatile */
8683 // FIXME: Enable this
8684 //if (!(cfg->cbb->last_ins && cfg->cbb->last_ins->opcode == OP_SEQ_POINT)) {
8685 NEW_SEQ_POINT (cfg, ins, ip - header->code, intr_loc);
8686 if ((sp != stack_start) && !sym_seq_point)
8687 ins->flags |= MONO_INST_NONEMPTY_STACK;
8688 MONO_ADD_INS (cfg->cbb, ins);
8691 mono_bitset_set_fast (seq_point_set_locs, ip - header->code);
8694 cfg->cbb->real_offset = cfg->real_offset;
8696 if ((cfg->method == method) && cfg->coverage_info) {
8697 guint32 cil_offset = ip - header->code;
8698 cfg->coverage_info->data [cil_offset].cil_code = ip;
8700 /* TODO: Use an increment here */
8701 #if defined(TARGET_X86)
8702 MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM);
8703 ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count);
8705 MONO_ADD_INS (cfg->cbb, ins);
8707 EMIT_NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
8708 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
8712 if (cfg->verbose_level > 3)
8713 printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
8717 if (seq_points && !sym_seq_points && sp != stack_start) {
8719 * The C# compiler uses these nops to notify the JIT that it should
8720 * insert seq points.
8722 NEW_SEQ_POINT (cfg, ins, ip - header->code, FALSE);
8723 MONO_ADD_INS (cfg->cbb, ins);
8725 if (cfg->keep_cil_nops)
8726 MONO_INST_NEW (cfg, ins, OP_HARD_NOP);
8728 MONO_INST_NEW (cfg, ins, OP_NOP);
8730 MONO_ADD_INS (cfg->cbb, ins);
8733 if (should_insert_brekpoint (cfg->method)) {
8734 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
8736 MONO_INST_NEW (cfg, ins, OP_NOP);
8739 MONO_ADD_INS (cfg->cbb, ins);
8745 CHECK_STACK_OVF (1);
8746 n = (*ip)-CEE_LDARG_0;
8748 EMIT_NEW_ARGLOAD (cfg, ins, n);
8756 CHECK_STACK_OVF (1);
8757 n = (*ip)-CEE_LDLOC_0;
8759 EMIT_NEW_LOCLOAD (cfg, ins, n);
8768 n = (*ip)-CEE_STLOC_0;
8771 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
8773 emit_stloc_ir (cfg, sp, header, n);
8780 CHECK_STACK_OVF (1);
8783 EMIT_NEW_ARGLOAD (cfg, ins, n);
8789 CHECK_STACK_OVF (1);
8792 NEW_ARGLOADA (cfg, ins, n);
8793 MONO_ADD_INS (cfg->cbb, ins);
8803 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
8805 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
8810 CHECK_STACK_OVF (1);
8813 EMIT_NEW_LOCLOAD (cfg, ins, n);
8817 case CEE_LDLOCA_S: {
8818 unsigned char *tmp_ip;
8820 CHECK_STACK_OVF (1);
8821 CHECK_LOCAL (ip [1]);
8823 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 1))) {
8829 EMIT_NEW_LOCLOADA (cfg, ins, ip [1]);
8838 CHECK_LOCAL (ip [1]);
8839 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
8841 emit_stloc_ir (cfg, sp, header, ip [1]);
8846 CHECK_STACK_OVF (1);
8847 EMIT_NEW_PCONST (cfg, ins, NULL);
8848 ins->type = STACK_OBJ;
8853 CHECK_STACK_OVF (1);
8854 EMIT_NEW_ICONST (cfg, ins, -1);
8867 CHECK_STACK_OVF (1);
8868 EMIT_NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
8874 CHECK_STACK_OVF (1);
8876 EMIT_NEW_ICONST (cfg, ins, *((signed char*)ip));
8882 CHECK_STACK_OVF (1);
8883 EMIT_NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
8889 CHECK_STACK_OVF (1);
8890 MONO_INST_NEW (cfg, ins, OP_I8CONST);
8891 ins->type = STACK_I8;
8892 ins->dreg = alloc_dreg (cfg, STACK_I8);
8894 ins->inst_l = (gint64)read64 (ip);
8895 MONO_ADD_INS (cfg->cbb, ins);
8901 gboolean use_aotconst = FALSE;
8903 #ifdef TARGET_POWERPC
8904 /* FIXME: Clean this up */
8905 if (cfg->compile_aot)
8906 use_aotconst = TRUE;
8909 /* FIXME: we should really allocate this only late in the compilation process */
8910 f = (float *)mono_domain_alloc (cfg->domain, sizeof (float));
8912 CHECK_STACK_OVF (1);
8918 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R4, f);
8920 dreg = alloc_freg (cfg);
8921 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR4_MEMBASE, dreg, cons->dreg, 0);
8922 ins->type = cfg->r4_stack_type;
8924 MONO_INST_NEW (cfg, ins, OP_R4CONST);
8925 ins->type = cfg->r4_stack_type;
8926 ins->dreg = alloc_dreg (cfg, STACK_R8);
8928 MONO_ADD_INS (cfg->cbb, ins);
8938 gboolean use_aotconst = FALSE;
8940 #ifdef TARGET_POWERPC
8941 /* FIXME: Clean this up */
8942 if (cfg->compile_aot)
8943 use_aotconst = TRUE;
8946 /* FIXME: we should really allocate this only late in the compilation process */
8947 d = (double *)mono_domain_alloc (cfg->domain, sizeof (double));
8949 CHECK_STACK_OVF (1);
8955 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R8, d);
8957 dreg = alloc_freg (cfg);
8958 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR8_MEMBASE, dreg, cons->dreg, 0);
8959 ins->type = STACK_R8;
8961 MONO_INST_NEW (cfg, ins, OP_R8CONST);
8962 ins->type = STACK_R8;
8963 ins->dreg = alloc_dreg (cfg, STACK_R8);
8965 MONO_ADD_INS (cfg->cbb, ins);
8974 MonoInst *temp, *store;
8976 CHECK_STACK_OVF (1);
8980 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
8981 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
8983 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8986 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8999 if (sp [0]->type == STACK_R8)
9000 /* we need to pop the value from the x86 FP stack */
9001 MONO_EMIT_NEW_UNALU (cfg, OP_X86_FPOP, -1, sp [0]->dreg);
9006 MonoMethodSignature *fsig;
9009 INLINE_FAILURE ("jmp");
9010 GSHAREDVT_FAILURE (*ip);
9013 if (stack_start != sp)
9015 token = read32 (ip + 1);
9016 /* FIXME: check the signature matches */
9017 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
9020 if (cfg->gshared && mono_method_check_context_used (cmethod))
9021 GENERIC_SHARING_FAILURE (CEE_JMP);
9023 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9025 fsig = mono_method_signature (cmethod);
9026 n = fsig->param_count + fsig->hasthis;
9027 if (cfg->llvm_only) {
9030 args = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
9031 for (i = 0; i < n; ++i)
9032 EMIT_NEW_ARGLOAD (cfg, args [i], i);
9033 ins = mono_emit_method_call_full (cfg, cmethod, fsig, TRUE, args, NULL, NULL, NULL);
9035 * The code in mono-basic-block.c treats the rest of the code as dead, but we
9036 * have to emit a normal return since llvm expects it.
9039 emit_setret (cfg, ins);
9040 MONO_INST_NEW (cfg, ins, OP_BR);
9041 ins->inst_target_bb = end_bblock;
9042 MONO_ADD_INS (cfg->cbb, ins);
9043 link_bblock (cfg, cfg->cbb, end_bblock);
9046 } else if (cfg->backend->have_op_tail_call) {
9047 /* Handle tail calls similarly to calls */
9050 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
9051 call->method = cmethod;
9052 call->tail_call = TRUE;
9053 call->signature = mono_method_signature (cmethod);
9054 call->args = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
9055 call->inst.inst_p0 = cmethod;
9056 for (i = 0; i < n; ++i)
9057 EMIT_NEW_ARGLOAD (cfg, call->args [i], i);
9059 mono_arch_emit_call (cfg, call);
9060 cfg->param_area = MAX(cfg->param_area, call->stack_usage);
9061 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
9063 for (i = 0; i < num_args; ++i)
9064 /* Prevent arguments from being optimized away */
9065 arg_array [i]->flags |= MONO_INST_VOLATILE;
9067 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9068 ins = (MonoInst*)call;
9069 ins->inst_p0 = cmethod;
9070 MONO_ADD_INS (cfg->cbb, ins);
9074 start_new_bblock = 1;
9079 MonoMethodSignature *fsig;
9082 token = read32 (ip + 1);
9086 //GSHAREDVT_FAILURE (*ip);
9091 fsig = mini_get_signature (method, token, generic_context, &cfg->error);
9094 if (method->dynamic && fsig->pinvoke) {
9098 * This is a call through a function pointer using a pinvoke
9099 * signature. Have to create a wrapper and call that instead.
9100 * FIXME: This is very slow, need to create a wrapper at JIT time
9101 * instead based on the signature.
9103 EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image);
9104 EMIT_NEW_PCONST (cfg, args [1], fsig);
9106 addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
9109 n = fsig->param_count + fsig->hasthis;
9113 //g_assert (!virtual_ || fsig->hasthis);
9117 inline_costs += 10 * num_calls++;
9120 * Making generic calls out of gsharedvt methods.
9121 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9122 * patching gshared method addresses into a gsharedvt method.
9124 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
9126 * We pass the address to the gsharedvt trampoline in the rgctx reg
9128 MonoInst *callee = addr;
9130 if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE)
9132 GSHAREDVT_FAILURE (*ip);
9136 GSHAREDVT_FAILURE (*ip);
9138 addr = emit_get_rgctx_sig (cfg, context_used,
9139 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
9140 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
9144 /* Prevent inlining of methods with indirect calls */
9145 INLINE_FAILURE ("indirect call");
9147 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) {
9148 MonoJumpInfoType info_type;
9152 * Instead of emitting an indirect call, emit a direct call
9153 * with the contents of the aotconst as the patch info.
9155 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) {
9156 info_type = (MonoJumpInfoType)addr->inst_c1;
9157 info_data = addr->inst_p0;
9159 info_type = (MonoJumpInfoType)addr->inst_right->inst_c1;
9160 info_data = addr->inst_right->inst_left;
9163 if (info_type == MONO_PATCH_INFO_ICALL_ADDR) {
9164 ins = (MonoInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_ICALL_ADDR_CALL, info_data, fsig, sp);
9167 } else if (info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
9168 ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp);
9173 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9177 /* End of call, INS should contain the result of the call, if any */
9179 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9181 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
9184 CHECK_CFG_EXCEPTION;
9188 constrained_class = NULL;
9192 case CEE_CALLVIRT: {
9193 MonoInst *addr = NULL;
9194 MonoMethodSignature *fsig = NULL;
9196 int virtual_ = *ip == CEE_CALLVIRT;
9197 gboolean pass_imt_from_rgctx = FALSE;
9198 MonoInst *imt_arg = NULL;
9199 MonoInst *keep_this_alive = NULL;
9200 gboolean pass_vtable = FALSE;
9201 gboolean pass_mrgctx = FALSE;
9202 MonoInst *vtable_arg = NULL;
9203 gboolean check_this = FALSE;
9204 gboolean supported_tail_call = FALSE;
9205 gboolean tail_call = FALSE;
9206 gboolean need_seq_point = FALSE;
9207 guint32 call_opcode = *ip;
9208 gboolean emit_widen = TRUE;
9209 gboolean push_res = TRUE;
9210 gboolean skip_ret = FALSE;
9211 gboolean delegate_invoke = FALSE;
9212 gboolean direct_icall = FALSE;
9213 gboolean constrained_partial_call = FALSE;
9214 MonoMethod *cil_method;
9217 token = read32 (ip + 1);
9221 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
9224 cil_method = cmethod;
9226 if (constrained_class) {
9227 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->gshared) {
9228 if (!mini_is_gsharedvt_klass (constrained_class)) {
9229 g_assert (!cmethod->klass->valuetype);
9230 if (!mini_type_is_reference (&constrained_class->byval_arg))
9231 constrained_partial_call = TRUE;
9235 if (method->wrapper_type != MONO_WRAPPER_NONE) {
9236 if (cfg->verbose_level > 2)
9237 printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_class));
9238 if (!((constrained_class->byval_arg.type == MONO_TYPE_VAR ||
9239 constrained_class->byval_arg.type == MONO_TYPE_MVAR) &&
9241 cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_class, generic_context, &cfg->error);
9245 if (cfg->verbose_level > 2)
9246 printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_class));
9248 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->gshared) {
9250 * This is needed since get_method_constrained can't find
9251 * the method in klass representing a type var.
9252 * The type var is guaranteed to be a reference type in this
9255 if (!mini_is_gsharedvt_klass (constrained_class))
9256 g_assert (!cmethod->klass->valuetype);
9258 cmethod = mono_get_method_constrained_checked (image, token, constrained_class, generic_context, &cil_method, &cfg->error);
9264 if (!dont_verify && !cfg->skip_visibility) {
9265 MonoMethod *target_method = cil_method;
9266 if (method->is_inflated) {
9267 target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context), &cfg->error);
9270 if (!mono_method_can_access_method (method_definition, target_method) &&
9271 !mono_method_can_access_method (method, cil_method))
9272 METHOD_ACCESS_FAILURE (method, cil_method);
9275 if (mono_security_core_clr_enabled ())
9276 ensure_method_is_allowed_to_call_method (cfg, method, cil_method);
9278 if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
9279 /* MS.NET seems to silently convert this to a callvirt */
9284 * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and
9285 * converts to a callvirt.
9287 * tests/bug-515884.il is an example of this behavior
9289 const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC;
9290 const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL;
9291 if (!virtual_ && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE)
9295 if (!cmethod->klass->inited)
9296 if (!mono_class_init (cmethod->klass))
9297 TYPE_LOAD_ERROR (cmethod->klass);
9299 fsig = mono_method_signature (cmethod);
9302 if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
9303 mini_class_is_system_array (cmethod->klass)) {
9304 array_rank = cmethod->klass->rank;
9305 } else if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && icall_is_direct_callable (cfg, cmethod)) {
9306 direct_icall = TRUE;
9307 } else if (fsig->pinvoke) {
9308 MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
9309 fsig = mono_method_signature (wrapper);
9310 } else if (constrained_class) {
9312 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
9316 if (cfg->llvm_only && !cfg->method->wrapper_type && (!cmethod || cmethod->is_inflated))
9317 cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, fsig);
9319 /* See code below */
9320 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9321 MonoBasicBlock *tbb;
9323 GET_BBLOCK (cfg, tbb, ip + 5);
9324 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9326 * We want to extend the try block to cover the call, but we can't do it if the
9327 * call is made directly since its followed by an exception check.
9329 direct_icall = FALSE;
9333 mono_save_token_info (cfg, image, token, cil_method);
9335 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code)))
9336 need_seq_point = TRUE;
9338 /* Don't support calls made using type arguments for now */
9340 if (cfg->gsharedvt) {
9341 if (mini_is_gsharedvt_signature (fsig))
9342 GSHAREDVT_FAILURE (*ip);
9346 if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
9347 g_assert_not_reached ();
9349 n = fsig->param_count + fsig->hasthis;
9351 if (!cfg->gshared && cmethod->klass->generic_container)
9355 g_assert (!mono_method_check_context_used (cmethod));
9359 //g_assert (!virtual_ || fsig->hasthis);
9364 * We have the `constrained.' prefix opcode.
9366 if (constrained_class) {
9367 if (mini_is_gsharedvt_klass (constrained_class)) {
9368 if ((cmethod->klass != mono_defaults.object_class) && constrained_class->valuetype && cmethod->klass->valuetype) {
9369 /* The 'Own method' case below */
9370 } else if (cmethod->klass->image != mono_defaults.corlib && !(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !cmethod->klass->valuetype) {
9371 /* 'The type parameter is instantiated as a reference type' case below. */
9373 ins = handle_constrained_gsharedvt_call (cfg, cmethod, fsig, sp, constrained_class, &emit_widen);
9374 CHECK_CFG_EXCEPTION;
9380 if (constrained_partial_call) {
9381 gboolean need_box = TRUE;
9384 * The receiver is a valuetype, but the exact type is not known at compile time. This means the
9385 * called method is not known at compile time either. The called method could end up being
9386 * one of the methods on the parent classes (object/valuetype/enum), in which case we need
9387 * to box the receiver.
9388 * A simple solution would be to box always and make a normal virtual call, but that would
9389 * be bad performance wise.
9391 if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE && cmethod->klass->generic_class) {
9393 * The parent classes implement no generic interfaces, so the called method will be a vtype method, so no boxing neccessary.
9398 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)) {
9399 /* The called method is not virtual, i.e. Object:GetType (), the receiver is a vtype, has to box */
9400 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9401 ins->klass = constrained_class;
9402 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9403 CHECK_CFG_EXCEPTION;
9404 } else if (need_box) {
9406 MonoBasicBlock *is_ref_bb, *end_bb;
9407 MonoInst *nonbox_call;
9410 * Determine at runtime whenever the called method is defined on object/valuetype/enum, and emit a boxing call
9412 * FIXME: It is possible to inline the called method in a lot of cases, i.e. for T_INT,
9413 * the no-box case goes to a method in Int32, while the box case goes to a method in Enum.
9415 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
9417 NEW_BBLOCK (cfg, is_ref_bb);
9418 NEW_BBLOCK (cfg, end_bb);
9420 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);
9421 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, box_type->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
9422 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
9425 nonbox_call = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9427 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
9430 MONO_START_BB (cfg, is_ref_bb);
9431 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9432 ins->klass = constrained_class;
9433 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9434 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9436 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
9438 MONO_START_BB (cfg, end_bb);
9441 nonbox_call->dreg = ins->dreg;
9444 g_assert (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
9445 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
9446 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9449 } else if (constrained_class->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
9451 * The type parameter is instantiated as a valuetype,
9452 * but that type doesn't override the method we're
9453 * calling, so we need to box `this'.
9455 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9456 ins->klass = constrained_class;
9457 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9458 CHECK_CFG_EXCEPTION;
9459 } else if (!constrained_class->valuetype) {
9460 int dreg = alloc_ireg_ref (cfg);
9463 * The type parameter is instantiated as a reference
9464 * type. We have a managed pointer on the stack, so
9465 * we need to dereference it here.
9467 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, sp [0]->dreg, 0);
9468 ins->type = STACK_OBJ;
9471 if (cmethod->klass->valuetype) {
9474 /* Interface method */
9477 mono_class_setup_vtable (constrained_class);
9478 CHECK_TYPELOAD (constrained_class);
9479 ioffset = mono_class_interface_offset (constrained_class, cmethod->klass);
9481 TYPE_LOAD_ERROR (constrained_class);
9482 slot = mono_method_get_vtable_slot (cmethod);
9484 TYPE_LOAD_ERROR (cmethod->klass);
9485 cmethod = constrained_class->vtable [ioffset + slot];
9487 if (cmethod->klass == mono_defaults.enum_class) {
9488 /* Enum implements some interfaces, so treat this as the first case */
9489 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9490 ins->klass = constrained_class;
9491 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9492 CHECK_CFG_EXCEPTION;
9497 constrained_class = NULL;
9500 if (check_call_signature (cfg, fsig, sp))
9503 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke"))
9504 delegate_invoke = TRUE;
9506 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) {
9507 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9508 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9516 * If the callee is a shared method, then its static cctor
9517 * might not get called after the call was patched.
9519 if (cfg->gshared && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
9520 emit_class_init (cfg, cmethod->klass);
9521 CHECK_TYPELOAD (cmethod->klass);
9524 check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx);
9527 MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
9529 context_used = mini_method_check_context_used (cfg, cmethod);
9531 if (context_used && (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
9532 /* Generic method interface
9533 calls are resolved via a
9534 helper function and don't
9536 if (!cmethod_context || !cmethod_context->method_inst)
9537 pass_imt_from_rgctx = TRUE;
9541 * If a shared method calls another
9542 * shared method then the caller must
9543 * have a generic sharing context
9544 * because the magic trampoline
9545 * requires it. FIXME: We shouldn't
9546 * have to force the vtable/mrgctx
9547 * variable here. Instead there
9548 * should be a flag in the cfg to
9549 * request a generic sharing context.
9552 ((method->flags & METHOD_ATTRIBUTE_STATIC) || method->klass->valuetype))
9553 mono_get_vtable_var (cfg);
9558 vtable_arg = emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE);
9560 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
9562 CHECK_TYPELOAD (cmethod->klass);
9563 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
9568 g_assert (!vtable_arg);
9570 if (!cfg->compile_aot) {
9572 * emit_get_rgctx_method () calls mono_class_vtable () so check
9573 * for type load errors before.
9575 mono_class_setup_vtable (cmethod->klass);
9576 CHECK_TYPELOAD (cmethod->klass);
9579 vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
9581 /* !marshalbyref is needed to properly handle generic methods + remoting */
9582 if ((!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
9583 MONO_METHOD_IS_FINAL (cmethod)) &&
9584 !mono_class_is_marshalbyref (cmethod->klass)) {
9591 if (pass_imt_from_rgctx) {
9592 g_assert (!pass_vtable);
9594 imt_arg = emit_get_rgctx_method (cfg, context_used,
9595 cmethod, MONO_RGCTX_INFO_METHOD);
9599 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9601 /* Calling virtual generic methods */
9602 if (virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
9603 !(MONO_METHOD_IS_FINAL (cmethod) &&
9604 cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) &&
9605 fsig->generic_param_count &&
9606 !(cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) &&
9608 MonoInst *this_temp, *this_arg_temp, *store;
9609 MonoInst *iargs [4];
9611 g_assert (fsig->is_inflated);
9613 /* Prevent inlining of methods that contain indirect calls */
9614 INLINE_FAILURE ("virtual generic call");
9616 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig))
9617 GSHAREDVT_FAILURE (*ip);
9619 if (cfg->backend->have_generalized_imt_thunk && cfg->backend->gshared_supported && cmethod->wrapper_type == MONO_WRAPPER_NONE) {
9620 g_assert (!imt_arg);
9622 g_assert (cmethod->is_inflated);
9623 imt_arg = emit_get_rgctx_method (cfg, context_used,
9624 cmethod, MONO_RGCTX_INFO_METHOD);
9625 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, sp [0], imt_arg, NULL);
9627 this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
9628 NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
9629 MONO_ADD_INS (cfg->cbb, store);
9631 /* FIXME: This should be a managed pointer */
9632 this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
9634 EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
9635 iargs [1] = emit_get_rgctx_method (cfg, context_used,
9636 cmethod, MONO_RGCTX_INFO_METHOD);
9637 EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
9638 addr = mono_emit_jit_icall (cfg,
9639 mono_helper_compile_generic_method, iargs);
9641 EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
9643 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9650 * Implement a workaround for the inherent races involved in locking:
9656 * If a thread abort happens between the call to Monitor.Enter () and the start of the
9657 * try block, the Exit () won't be executed, see:
9658 * http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
9659 * To work around this, we extend such try blocks to include the last x bytes
9660 * of the Monitor.Enter () call.
9662 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9663 MonoBasicBlock *tbb;
9665 GET_BBLOCK (cfg, tbb, ip + 5);
9667 * Only extend try blocks with a finally, to avoid catching exceptions thrown
9668 * from Monitor.Enter like ArgumentNullException.
9670 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9671 /* Mark this bblock as needing to be extended */
9672 tbb->extend_try_block = TRUE;
9676 /* Conversion to a JIT intrinsic */
9677 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) {
9678 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9679 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9687 if ((cfg->opt & MONO_OPT_INLINE) &&
9688 (!virtual_ || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
9689 mono_method_check_inlining (cfg, cmethod)) {
9691 gboolean always = FALSE;
9693 if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
9694 (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
9695 /* Prevent inlining of methods that call wrappers */
9696 INLINE_FAILURE ("wrapper call");
9697 cmethod = mono_marshal_get_native_wrapper (cmethod, TRUE, FALSE);
9701 costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, always);
9703 cfg->real_offset += 5;
9705 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9706 /* *sp is already set by inline_method */
9711 inline_costs += costs;
9717 /* Tail recursion elimination */
9718 if ((cfg->opt & MONO_OPT_TAILC) && call_opcode == CEE_CALL && cmethod == method && ip [5] == CEE_RET && !vtable_arg) {
9719 gboolean has_vtargs = FALSE;
9722 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9723 INLINE_FAILURE ("tail call");
9725 /* keep it simple */
9726 for (i = fsig->param_count - 1; i >= 0; i--) {
9727 if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
9732 for (i = 0; i < n; ++i)
9733 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9734 MONO_INST_NEW (cfg, ins, OP_BR);
9735 MONO_ADD_INS (cfg->cbb, ins);
9736 tblock = start_bblock->out_bb [0];
9737 link_bblock (cfg, cfg->cbb, tblock);
9738 ins->inst_target_bb = tblock;
9739 start_new_bblock = 1;
9741 /* skip the CEE_RET, too */
9742 if (ip_in_bb (cfg, cfg->cbb, ip + 5))
9749 inline_costs += 10 * num_calls++;
9752 * Making generic calls out of gsharedvt methods.
9753 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9754 * patching gshared method addresses into a gsharedvt method.
9756 if (cfg->gsharedvt && (mini_is_gsharedvt_signature (fsig) || cmethod->is_inflated || cmethod->klass->generic_class) &&
9757 !(cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY) &&
9758 (!(cfg->llvm_only && virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)))) {
9759 MonoRgctxInfoType info_type;
9762 //if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)
9763 //GSHAREDVT_FAILURE (*ip);
9764 // disable for possible remoting calls
9765 if (fsig->hasthis && (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class))
9766 GSHAREDVT_FAILURE (*ip);
9767 if (fsig->generic_param_count) {
9768 /* virtual generic call */
9769 g_assert (!imt_arg);
9770 /* Same as the virtual generic case above */
9771 imt_arg = emit_get_rgctx_method (cfg, context_used,
9772 cmethod, MONO_RGCTX_INFO_METHOD);
9773 /* This is not needed, as the trampoline code will pass one, and it might be passed in the same reg as the imt arg */
9775 } else if ((cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !imt_arg) {
9776 /* This can happen when we call a fully instantiated iface method */
9777 imt_arg = emit_get_rgctx_method (cfg, context_used,
9778 cmethod, MONO_RGCTX_INFO_METHOD);
9783 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (cmethod->name, "Invoke")))
9784 keep_this_alive = sp [0];
9786 if (virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))
9787 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
9789 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE;
9790 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, info_type);
9792 if (cfg->llvm_only) {
9793 // FIXME: Avoid initializing vtable_arg
9794 ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
9796 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9801 /* Generic sharing */
9804 * Use this if the callee is gsharedvt sharable too, since
9805 * at runtime we might find an instantiation so the call cannot
9806 * be patched (the 'no_patch' code path in mini-trampolines.c).
9808 if (context_used && !imt_arg && !array_rank && !delegate_invoke &&
9809 (!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
9810 !mono_class_generic_sharing_enabled (cmethod->klass)) &&
9811 (!virtual_ || MONO_METHOD_IS_FINAL (cmethod) ||
9812 !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) {
9813 INLINE_FAILURE ("gshared");
9815 g_assert (cfg->gshared && cmethod);
9819 * We are compiling a call to a
9820 * generic method from shared code,
9821 * which means that we have to look up
9822 * the method in the rgctx and do an
9826 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9828 if (cfg->llvm_only) {
9829 if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig))
9830 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER);
9832 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9833 // FIXME: Avoid initializing imt_arg/vtable_arg
9834 ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
9836 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9837 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9842 /* Direct calls to icalls */
9844 MonoMethod *wrapper;
9847 /* Inline the wrapper */
9848 wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
9850 costs = inline_method (cfg, wrapper, fsig, sp, ip, cfg->real_offset, TRUE);
9851 g_assert (costs > 0);
9852 cfg->real_offset += 5;
9854 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9855 /* *sp is already set by inline_method */
9860 inline_costs += costs;
9869 if (strcmp (cmethod->name, "Set") == 0) { /* array Set */
9870 MonoInst *val = sp [fsig->param_count];
9872 if (val->type == STACK_OBJ) {
9873 MonoInst *iargs [2];
9878 mono_emit_jit_icall (cfg, mono_helper_stelem_ref_check, iargs);
9881 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, TRUE);
9882 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, fsig->params [fsig->param_count - 1], addr->dreg, 0, val->dreg);
9883 if (cfg->gen_write_barriers && val->type == STACK_OBJ && !(val->opcode == OP_PCONST && val->inst_c0 == 0))
9884 emit_write_barrier (cfg, addr, val);
9885 if (cfg->gen_write_barriers && mini_is_gsharedvt_klass (cmethod->klass))
9886 GSHAREDVT_FAILURE (*ip);
9887 } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
9888 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9890 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, addr->dreg, 0);
9891 } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
9892 if (!cmethod->klass->element_class->valuetype && !readonly)
9893 mini_emit_check_array_type (cfg, sp [0], cmethod->klass);
9894 CHECK_TYPELOAD (cmethod->klass);
9897 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9900 g_assert_not_reached ();
9907 ins = mini_redirect_call (cfg, cmethod, fsig, sp, virtual_ ? sp [0] : NULL);
9911 /* Tail prefix / tail call optimization */
9913 /* FIXME: Enabling TAILC breaks some inlining/stack trace/etc tests */
9914 /* FIXME: runtime generic context pointer for jumps? */
9915 /* FIXME: handle this for generic sharing eventually */
9916 if ((ins_flag & MONO_INST_TAILCALL) &&
9917 !vtable_arg && !cfg->gshared && is_supported_tail_call (cfg, method, cmethod, fsig, call_opcode))
9918 supported_tail_call = TRUE;
9920 if (supported_tail_call) {
9923 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9924 INLINE_FAILURE ("tail call");
9926 //printf ("HIT: %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
9928 if (cfg->backend->have_op_tail_call) {
9929 /* Handle tail calls similarly to normal calls */
9932 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9934 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9935 call->tail_call = TRUE;
9936 call->method = cmethod;
9937 call->signature = mono_method_signature (cmethod);
9940 * We implement tail calls by storing the actual arguments into the
9941 * argument variables, then emitting a CEE_JMP.
9943 for (i = 0; i < n; ++i) {
9944 /* Prevent argument from being register allocated */
9945 arg_array [i]->flags |= MONO_INST_VOLATILE;
9946 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9948 ins = (MonoInst*)call;
9949 ins->inst_p0 = cmethod;
9950 ins->inst_p1 = arg_array [0];
9951 MONO_ADD_INS (cfg->cbb, ins);
9952 link_bblock (cfg, cfg->cbb, end_bblock);
9953 start_new_bblock = 1;
9955 // FIXME: Eliminate unreachable epilogs
9958 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9959 * only reachable from this call.
9961 GET_BBLOCK (cfg, tblock, ip + 5);
9962 if (tblock == cfg->cbb || tblock->in_count == 0)
9971 * Synchronized wrappers.
9972 * Its hard to determine where to replace a method with its synchronized
9973 * wrapper without causing an infinite recursion. The current solution is
9974 * to add the synchronized wrapper in the trampolines, and to
9975 * change the called method to a dummy wrapper, and resolve that wrapper
9976 * to the real method in mono_jit_compile_method ().
9978 if (cfg->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
9979 MonoMethod *orig = mono_marshal_method_from_wrapper (cfg->method);
9980 if (cmethod == orig || (cmethod->is_inflated && mono_method_get_declaring_generic_method (cmethod) == orig))
9981 cmethod = mono_marshal_get_synchronized_inner_wrapper (cmethod);
9985 * Virtual calls in llvm-only mode.
9987 if (cfg->llvm_only && virtual_ && cmethod && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
9988 ins = emit_llvmonly_virtual_call (cfg, cmethod, fsig, context_used, sp);
9993 INLINE_FAILURE ("call");
9994 ins = mono_emit_method_call_full (cfg, cmethod, fsig, tail_call, sp, virtual_ ? sp [0] : NULL,
9995 imt_arg, vtable_arg);
9997 if (tail_call && !cfg->llvm_only) {
9998 link_bblock (cfg, cfg->cbb, end_bblock);
9999 start_new_bblock = 1;
10001 // FIXME: Eliminate unreachable epilogs
10004 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
10005 * only reachable from this call.
10007 GET_BBLOCK (cfg, tblock, ip + 5);
10008 if (tblock == cfg->cbb || tblock->in_count == 0)
10015 /* End of call, INS should contain the result of the call, if any */
10017 if (push_res && !MONO_TYPE_IS_VOID (fsig->ret)) {
10020 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
10025 if (keep_this_alive) {
10026 MonoInst *dummy_use;
10028 /* See mono_emit_method_call_full () */
10029 EMIT_NEW_DUMMY_USE (cfg, dummy_use, keep_this_alive);
10032 CHECK_CFG_EXCEPTION;
10036 g_assert (*ip == CEE_RET);
10040 constrained_class = NULL;
10041 if (need_seq_point)
10042 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10046 if (cfg->method != method) {
10047 /* return from inlined method */
10049 * If in_count == 0, that means the ret is unreachable due to
10050 * being preceeded by a throw. In that case, inline_method () will
10051 * handle setting the return value
10052 * (test case: test_0_inline_throw ()).
10054 if (return_var && cfg->cbb->in_count) {
10055 MonoType *ret_type = mono_method_signature (method)->ret;
10061 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
10064 //g_assert (returnvar != -1);
10065 EMIT_NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
10066 cfg->ret_var_set = TRUE;
10069 emit_instrumentation_call (cfg, mono_profiler_method_leave);
10071 if (cfg->lmf_var && cfg->cbb->in_count && !cfg->llvm_only)
10072 emit_pop_lmf (cfg);
10075 MonoType *ret_type = mini_get_underlying_type (mono_method_signature (method)->ret);
10077 if (seq_points && !sym_seq_points) {
10079 * Place a seq point here too even through the IL stack is not
10080 * empty, so a step over on
10083 * will work correctly.
10085 NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
10086 MONO_ADD_INS (cfg->cbb, ins);
10089 g_assert (!return_var);
10093 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
10096 emit_setret (cfg, *sp);
10099 if (sp != stack_start)
10101 MONO_INST_NEW (cfg, ins, OP_BR);
10103 ins->inst_target_bb = end_bblock;
10104 MONO_ADD_INS (cfg->cbb, ins);
10105 link_bblock (cfg, cfg->cbb, end_bblock);
10106 start_new_bblock = 1;
10110 MONO_INST_NEW (cfg, ins, OP_BR);
10112 target = ip + 1 + (signed char)(*ip);
10114 GET_BBLOCK (cfg, tblock, target);
10115 link_bblock (cfg, cfg->cbb, tblock);
10116 ins->inst_target_bb = tblock;
10117 if (sp != stack_start) {
10118 handle_stack_args (cfg, stack_start, sp - stack_start);
10120 CHECK_UNVERIFIABLE (cfg);
10122 MONO_ADD_INS (cfg->cbb, ins);
10123 start_new_bblock = 1;
10124 inline_costs += BRANCH_COST;
10138 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
10140 target = ip + 1 + *(signed char*)ip;
10143 ADD_BINCOND (NULL);
10146 inline_costs += BRANCH_COST;
10150 MONO_INST_NEW (cfg, ins, OP_BR);
10153 target = ip + 4 + (gint32)read32(ip);
10155 GET_BBLOCK (cfg, tblock, target);
10156 link_bblock (cfg, cfg->cbb, tblock);
10157 ins->inst_target_bb = tblock;
10158 if (sp != stack_start) {
10159 handle_stack_args (cfg, stack_start, sp - stack_start);
10161 CHECK_UNVERIFIABLE (cfg);
10164 MONO_ADD_INS (cfg->cbb, ins);
10166 start_new_bblock = 1;
10167 inline_costs += BRANCH_COST;
10169 case CEE_BRFALSE_S:
10174 gboolean is_short = ((*ip) == CEE_BRFALSE_S) || ((*ip) == CEE_BRTRUE_S);
10175 gboolean is_true = ((*ip) == CEE_BRTRUE_S) || ((*ip) == CEE_BRTRUE);
10176 guint32 opsize = is_short ? 1 : 4;
10178 CHECK_OPSIZE (opsize);
10180 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
10183 target = ip + opsize + (is_short ? *(signed char*)ip : (gint32)read32(ip));
10188 GET_BBLOCK (cfg, tblock, target);
10189 link_bblock (cfg, cfg->cbb, tblock);
10190 GET_BBLOCK (cfg, tblock, ip);
10191 link_bblock (cfg, cfg->cbb, tblock);
10193 if (sp != stack_start) {
10194 handle_stack_args (cfg, stack_start, sp - stack_start);
10195 CHECK_UNVERIFIABLE (cfg);
10198 MONO_INST_NEW(cfg, cmp, OP_ICOMPARE_IMM);
10199 cmp->sreg1 = sp [0]->dreg;
10200 type_from_op (cfg, cmp, sp [0], NULL);
10203 #if SIZEOF_REGISTER == 4
10204 if (cmp->opcode == OP_LCOMPARE_IMM) {
10205 /* Convert it to OP_LCOMPARE */
10206 MONO_INST_NEW (cfg, ins, OP_I8CONST);
10207 ins->type = STACK_I8;
10208 ins->dreg = alloc_dreg (cfg, STACK_I8);
10210 MONO_ADD_INS (cfg->cbb, ins);
10211 cmp->opcode = OP_LCOMPARE;
10212 cmp->sreg2 = ins->dreg;
10215 MONO_ADD_INS (cfg->cbb, cmp);
10217 MONO_INST_NEW (cfg, ins, is_true ? CEE_BNE_UN : CEE_BEQ);
10218 type_from_op (cfg, ins, sp [0], NULL);
10219 MONO_ADD_INS (cfg->cbb, ins);
10220 ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);
10221 GET_BBLOCK (cfg, tblock, target);
10222 ins->inst_true_bb = tblock;
10223 GET_BBLOCK (cfg, tblock, ip);
10224 ins->inst_false_bb = tblock;
10225 start_new_bblock = 2;
10228 inline_costs += BRANCH_COST;
10243 MONO_INST_NEW (cfg, ins, *ip);
10245 target = ip + 4 + (gint32)read32(ip);
10248 ADD_BINCOND (NULL);
10251 inline_costs += BRANCH_COST;
10255 MonoBasicBlock **targets;
10256 MonoBasicBlock *default_bblock;
10257 MonoJumpInfoBBTable *table;
10258 int offset_reg = alloc_preg (cfg);
10259 int target_reg = alloc_preg (cfg);
10260 int table_reg = alloc_preg (cfg);
10261 int sum_reg = alloc_preg (cfg);
10262 gboolean use_op_switch;
10266 n = read32 (ip + 1);
10269 if ((src1->type != STACK_I4) && (src1->type != STACK_PTR))
10273 CHECK_OPSIZE (n * sizeof (guint32));
10274 target = ip + n * sizeof (guint32);
10276 GET_BBLOCK (cfg, default_bblock, target);
10277 default_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
10279 targets = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * n);
10280 for (i = 0; i < n; ++i) {
10281 GET_BBLOCK (cfg, tblock, target + (gint32)read32(ip));
10282 targets [i] = tblock;
10283 targets [i]->flags |= BB_INDIRECT_JUMP_TARGET;
10287 if (sp != stack_start) {
10289 * Link the current bb with the targets as well, so handle_stack_args
10290 * will set their in_stack correctly.
10292 link_bblock (cfg, cfg->cbb, default_bblock);
10293 for (i = 0; i < n; ++i)
10294 link_bblock (cfg, cfg->cbb, targets [i]);
10296 handle_stack_args (cfg, stack_start, sp - stack_start);
10298 CHECK_UNVERIFIABLE (cfg);
10300 /* Undo the links */
10301 mono_unlink_bblock (cfg, cfg->cbb, default_bblock);
10302 for (i = 0; i < n; ++i)
10303 mono_unlink_bblock (cfg, cfg->cbb, targets [i]);
10306 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, src1->dreg, n);
10307 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE_UN, default_bblock);
10309 for (i = 0; i < n; ++i)
10310 link_bblock (cfg, cfg->cbb, targets [i]);
10312 table = (MonoJumpInfoBBTable *)mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable));
10313 table->table = targets;
10314 table->table_size = n;
10316 use_op_switch = FALSE;
10318 /* ARM implements SWITCH statements differently */
10319 /* FIXME: Make it use the generic implementation */
10320 if (!cfg->compile_aot)
10321 use_op_switch = TRUE;
10324 if (COMPILE_LLVM (cfg))
10325 use_op_switch = TRUE;
10327 cfg->cbb->has_jump_table = 1;
10329 if (use_op_switch) {
10330 MONO_INST_NEW (cfg, ins, OP_SWITCH);
10331 ins->sreg1 = src1->dreg;
10332 ins->inst_p0 = table;
10333 ins->inst_many_bb = targets;
10334 ins->klass = (MonoClass *)GUINT_TO_POINTER (n);
10335 MONO_ADD_INS (cfg->cbb, ins);
10337 if (sizeof (gpointer) == 8)
10338 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 3);
10340 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 2);
10342 #if SIZEOF_REGISTER == 8
10343 /* The upper word might not be zero, and we add it to a 64 bit address later */
10344 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, offset_reg, offset_reg);
10347 if (cfg->compile_aot) {
10348 MONO_EMIT_NEW_AOTCONST (cfg, table_reg, table, MONO_PATCH_INFO_SWITCH);
10350 MONO_INST_NEW (cfg, ins, OP_JUMP_TABLE);
10351 ins->inst_c1 = MONO_PATCH_INFO_SWITCH;
10352 ins->inst_p0 = table;
10353 ins->dreg = table_reg;
10354 MONO_ADD_INS (cfg->cbb, ins);
10357 /* FIXME: Use load_memindex */
10358 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, table_reg, offset_reg);
10359 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, target_reg, sum_reg, 0);
10360 MONO_EMIT_NEW_UNALU (cfg, OP_BR_REG, -1, target_reg);
10362 start_new_bblock = 1;
10363 inline_costs += (BRANCH_COST * 2);
10376 case CEE_LDIND_REF:
10383 dreg = alloc_freg (cfg);
10386 dreg = alloc_lreg (cfg);
10388 case CEE_LDIND_REF:
10389 dreg = alloc_ireg_ref (cfg);
10392 dreg = alloc_preg (cfg);
10395 NEW_LOAD_MEMBASE (cfg, ins, ldind_to_load_membase (*ip), dreg, sp [0]->dreg, 0);
10396 ins->type = ldind_type [*ip - CEE_LDIND_I1];
10397 if (*ip == CEE_LDIND_R4)
10398 ins->type = cfg->r4_stack_type;
10399 ins->flags |= ins_flag;
10400 MONO_ADD_INS (cfg->cbb, ins);
10402 if (ins_flag & MONO_INST_VOLATILE) {
10403 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10404 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10409 case CEE_STIND_REF:
10420 if (ins_flag & MONO_INST_VOLATILE) {
10421 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
10422 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
10425 NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
10426 ins->flags |= ins_flag;
10429 MONO_ADD_INS (cfg->cbb, ins);
10431 if (cfg->gen_write_barriers && *ip == CEE_STIND_REF && method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && !((sp [1]->opcode == OP_PCONST) && (sp [1]->inst_p0 == 0)))
10432 emit_write_barrier (cfg, sp [0], sp [1]);
10441 MONO_INST_NEW (cfg, ins, (*ip));
10443 ins->sreg1 = sp [0]->dreg;
10444 ins->sreg2 = sp [1]->dreg;
10445 type_from_op (cfg, ins, sp [0], sp [1]);
10447 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type);
10449 /* Use the immediate opcodes if possible */
10450 if ((sp [1]->opcode == OP_ICONST) && mono_arch_is_inst_imm (sp [1]->inst_c0)) {
10451 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
10452 if (imm_opcode != -1) {
10453 ins->opcode = imm_opcode;
10454 ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0);
10457 NULLIFY_INS (sp [1]);
10461 MONO_ADD_INS ((cfg)->cbb, (ins));
10463 *sp++ = mono_decompose_opcode (cfg, ins);
10480 MONO_INST_NEW (cfg, ins, (*ip));
10482 ins->sreg1 = sp [0]->dreg;
10483 ins->sreg2 = sp [1]->dreg;
10484 type_from_op (cfg, ins, sp [0], sp [1]);
10486 add_widen_op (cfg, ins, &sp [0], &sp [1]);
10487 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type);
10489 /* FIXME: Pass opcode to is_inst_imm */
10491 /* Use the immediate opcodes if possible */
10492 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)) {
10493 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
10494 if (imm_opcode != -1) {
10495 ins->opcode = imm_opcode;
10496 if (sp [1]->opcode == OP_I8CONST) {
10497 #if SIZEOF_REGISTER == 8
10498 ins->inst_imm = sp [1]->inst_l;
10500 ins->inst_ls_word = sp [1]->inst_ls_word;
10501 ins->inst_ms_word = sp [1]->inst_ms_word;
10505 ins->inst_imm = (gssize)(sp [1]->inst_c0);
10508 /* Might be followed by an instruction added by add_widen_op */
10509 if (sp [1]->next == NULL)
10510 NULLIFY_INS (sp [1]);
10513 MONO_ADD_INS ((cfg)->cbb, (ins));
10515 *sp++ = mono_decompose_opcode (cfg, ins);
10528 case CEE_CONV_OVF_I8:
10529 case CEE_CONV_OVF_U8:
10530 case CEE_CONV_R_UN:
10533 /* Special case this earlier so we have long constants in the IR */
10534 if ((((*ip) == CEE_CONV_I8) || ((*ip) == CEE_CONV_U8)) && (sp [-1]->opcode == OP_ICONST)) {
10535 int data = sp [-1]->inst_c0;
10536 sp [-1]->opcode = OP_I8CONST;
10537 sp [-1]->type = STACK_I8;
10538 #if SIZEOF_REGISTER == 8
10539 if ((*ip) == CEE_CONV_U8)
10540 sp [-1]->inst_c0 = (guint32)data;
10542 sp [-1]->inst_c0 = data;
10544 sp [-1]->inst_ls_word = data;
10545 if ((*ip) == CEE_CONV_U8)
10546 sp [-1]->inst_ms_word = 0;
10548 sp [-1]->inst_ms_word = (data < 0) ? -1 : 0;
10550 sp [-1]->dreg = alloc_dreg (cfg, STACK_I8);
10557 case CEE_CONV_OVF_I4:
10558 case CEE_CONV_OVF_I1:
10559 case CEE_CONV_OVF_I2:
10560 case CEE_CONV_OVF_I:
10561 case CEE_CONV_OVF_U:
10564 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10565 ADD_UNOP (CEE_CONV_OVF_I8);
10572 case CEE_CONV_OVF_U1:
10573 case CEE_CONV_OVF_U2:
10574 case CEE_CONV_OVF_U4:
10577 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10578 ADD_UNOP (CEE_CONV_OVF_U8);
10585 case CEE_CONV_OVF_I1_UN:
10586 case CEE_CONV_OVF_I2_UN:
10587 case CEE_CONV_OVF_I4_UN:
10588 case CEE_CONV_OVF_I8_UN:
10589 case CEE_CONV_OVF_U1_UN:
10590 case CEE_CONV_OVF_U2_UN:
10591 case CEE_CONV_OVF_U4_UN:
10592 case CEE_CONV_OVF_U8_UN:
10593 case CEE_CONV_OVF_I_UN:
10594 case CEE_CONV_OVF_U_UN:
10601 CHECK_CFG_EXCEPTION;
10605 case CEE_ADD_OVF_UN:
10607 case CEE_MUL_OVF_UN:
10609 case CEE_SUB_OVF_UN:
10615 GSHAREDVT_FAILURE (*ip);
10618 token = read32 (ip + 1);
10619 klass = mini_get_class (method, token, generic_context);
10620 CHECK_TYPELOAD (klass);
10622 if (generic_class_is_reference_type (cfg, klass)) {
10623 MonoInst *store, *load;
10624 int dreg = alloc_ireg_ref (cfg);
10626 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, sp [1]->dreg, 0);
10627 load->flags |= ins_flag;
10628 MONO_ADD_INS (cfg->cbb, load);
10630 NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, sp [0]->dreg, 0, dreg);
10631 store->flags |= ins_flag;
10632 MONO_ADD_INS (cfg->cbb, store);
10634 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER)
10635 emit_write_barrier (cfg, sp [0], sp [1]);
10637 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10643 int loc_index = -1;
10649 token = read32 (ip + 1);
10650 klass = mini_get_class (method, token, generic_context);
10651 CHECK_TYPELOAD (klass);
10653 /* Optimize the common ldobj+stloc combination */
10656 loc_index = ip [6];
10663 loc_index = ip [5] - CEE_STLOC_0;
10670 if ((loc_index != -1) && ip_in_bb (cfg, cfg->cbb, ip + 5)) {
10671 CHECK_LOCAL (loc_index);
10673 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10674 ins->dreg = cfg->locals [loc_index]->dreg;
10675 ins->flags |= ins_flag;
10678 if (ins_flag & MONO_INST_VOLATILE) {
10679 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10680 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10686 /* Optimize the ldobj+stobj combination */
10687 /* The reference case ends up being a load+store anyway */
10688 /* Skip this if the operation is volatile. */
10689 if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 5) && read32 (ip + 6) == token) && !generic_class_is_reference_type (cfg, klass) && !(ins_flag & MONO_INST_VOLATILE)) {
10694 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10701 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10702 ins->flags |= ins_flag;
10705 if (ins_flag & MONO_INST_VOLATILE) {
10706 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10707 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10716 CHECK_STACK_OVF (1);
10718 n = read32 (ip + 1);
10720 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
10721 EMIT_NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
10722 ins->type = STACK_OBJ;
10725 else if (method->wrapper_type != MONO_WRAPPER_NONE) {
10726 MonoInst *iargs [1];
10727 char *str = (char *)mono_method_get_wrapper_data (method, n);
10729 if (cfg->compile_aot)
10730 EMIT_NEW_LDSTRLITCONST (cfg, iargs [0], str);
10732 EMIT_NEW_PCONST (cfg, iargs [0], str);
10733 *sp = mono_emit_jit_icall (cfg, mono_string_new_wrapper, iargs);
10735 if (cfg->opt & MONO_OPT_SHARED) {
10736 MonoInst *iargs [3];
10738 if (cfg->compile_aot) {
10739 cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n));
10741 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
10742 EMIT_NEW_IMAGECONST (cfg, iargs [1], image);
10743 EMIT_NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
10744 *sp = mono_emit_jit_icall (cfg, ves_icall_mono_ldstr, iargs);
10745 mono_ldstr_checked (cfg->domain, image, mono_metadata_token_index (n), &cfg->error);
10748 if (cfg->cbb->out_of_line) {
10749 MonoInst *iargs [2];
10751 if (image == mono_defaults.corlib) {
10753 * Avoid relocations in AOT and save some space by using a
10754 * version of helper_ldstr specialized to mscorlib.
10756 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (n));
10757 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr_mscorlib, iargs);
10759 /* Avoid creating the string object */
10760 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
10761 EMIT_NEW_ICONST (cfg, iargs [1], mono_metadata_token_index (n));
10762 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr, iargs);
10766 if (cfg->compile_aot) {
10767 NEW_LDSTRCONST (cfg, ins, image, n);
10769 MONO_ADD_INS (cfg->cbb, ins);
10772 NEW_PCONST (cfg, ins, NULL);
10773 ins->type = STACK_OBJ;
10774 ins->inst_p0 = mono_ldstr_checked (cfg->domain, image, mono_metadata_token_index (n), &cfg->error);
10778 OUT_OF_MEMORY_FAILURE;
10781 MONO_ADD_INS (cfg->cbb, ins);
10790 MonoInst *iargs [2];
10791 MonoMethodSignature *fsig;
10794 MonoInst *vtable_arg = NULL;
10797 token = read32 (ip + 1);
10798 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
10801 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
10804 mono_save_token_info (cfg, image, token, cmethod);
10806 if (!mono_class_init (cmethod->klass))
10807 TYPE_LOAD_ERROR (cmethod->klass);
10809 context_used = mini_method_check_context_used (cfg, cmethod);
10811 if (mono_security_core_clr_enabled ())
10812 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
10814 if (cfg->gshared && cmethod && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
10815 emit_class_init (cfg, cmethod->klass);
10816 CHECK_TYPELOAD (cmethod->klass);
10820 if (cfg->gsharedvt) {
10821 if (mini_is_gsharedvt_variable_signature (sig))
10822 GSHAREDVT_FAILURE (*ip);
10826 n = fsig->param_count;
10830 * Generate smaller code for the common newobj <exception> instruction in
10831 * argument checking code.
10833 if (cfg->cbb->out_of_line && cmethod->klass->image == mono_defaults.corlib &&
10834 is_exception_class (cmethod->klass) && n <= 2 &&
10835 ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) &&
10836 ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
10837 MonoInst *iargs [3];
10841 EMIT_NEW_ICONST (cfg, iargs [0], cmethod->klass->type_token);
10844 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_0, iargs);
10847 iargs [1] = sp [0];
10848 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_1, iargs);
10851 iargs [1] = sp [0];
10852 iargs [2] = sp [1];
10853 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_2, iargs);
10856 g_assert_not_reached ();
10864 /* move the args to allow room for 'this' in the first position */
10870 /* check_call_signature () requires sp[0] to be set */
10871 this_ins.type = STACK_OBJ;
10872 sp [0] = &this_ins;
10873 if (check_call_signature (cfg, fsig, sp))
10878 if (mini_class_is_system_array (cmethod->klass)) {
10879 *sp = emit_get_rgctx_method (cfg, context_used,
10880 cmethod, MONO_RGCTX_INFO_METHOD);
10882 /* Avoid varargs in the common case */
10883 if (fsig->param_count == 1)
10884 alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
10885 else if (fsig->param_count == 2)
10886 alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
10887 else if (fsig->param_count == 3)
10888 alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp);
10889 else if (fsig->param_count == 4)
10890 alloc = mono_emit_jit_icall (cfg, mono_array_new_4, sp);
10892 alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
10893 } else if (cmethod->string_ctor) {
10894 g_assert (!context_used);
10895 g_assert (!vtable_arg);
10896 /* we simply pass a null pointer */
10897 EMIT_NEW_PCONST (cfg, *sp, NULL);
10898 /* now call the string ctor */
10899 alloc = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, NULL, NULL, NULL);
10901 if (cmethod->klass->valuetype) {
10902 iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
10903 emit_init_rvar (cfg, iargs [0]->dreg, &cmethod->klass->byval_arg);
10904 EMIT_NEW_TEMPLOADA (cfg, *sp, iargs [0]->inst_c0);
10909 * The code generated by mini_emit_virtual_call () expects
10910 * iargs [0] to be a boxed instance, but luckily the vcall
10911 * will be transformed into a normal call there.
10913 } else if (context_used) {
10914 alloc = handle_alloc (cfg, cmethod->klass, FALSE, context_used);
10917 MonoVTable *vtable = NULL;
10919 if (!cfg->compile_aot)
10920 vtable = mono_class_vtable (cfg->domain, cmethod->klass);
10921 CHECK_TYPELOAD (cmethod->klass);
10924 * TypeInitializationExceptions thrown from the mono_runtime_class_init
10925 * call in mono_jit_runtime_invoke () can abort the finalizer thread.
10926 * As a workaround, we call class cctors before allocating objects.
10928 if (mini_field_access_needs_cctor_run (cfg, method, cmethod->klass, vtable) && !(g_slist_find (class_inits, cmethod->klass))) {
10929 emit_class_init (cfg, cmethod->klass);
10930 if (cfg->verbose_level > 2)
10931 printf ("class %s.%s needs init call for ctor\n", cmethod->klass->name_space, cmethod->klass->name);
10932 class_inits = g_slist_prepend (class_inits, cmethod->klass);
10935 alloc = handle_alloc (cfg, cmethod->klass, FALSE, 0);
10938 CHECK_CFG_EXCEPTION; /*for handle_alloc*/
10941 MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, alloc->dreg);
10943 /* Now call the actual ctor */
10944 handle_ctor_call (cfg, cmethod, fsig, context_used, sp, ip, &inline_costs);
10945 CHECK_CFG_EXCEPTION;
10948 if (alloc == NULL) {
10950 EMIT_NEW_TEMPLOAD (cfg, ins, iargs [0]->inst_c0);
10951 type_to_eval_stack_type (cfg, &ins->klass->byval_arg, ins);
10959 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip - header->code)))
10960 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10963 case CEE_CASTCLASS:
10967 token = read32 (ip + 1);
10968 klass = mini_get_class (method, token, generic_context);
10969 CHECK_TYPELOAD (klass);
10970 if (sp [0]->type != STACK_OBJ)
10973 ins = handle_castclass (cfg, klass, *sp, ip, &inline_costs);
10974 CHECK_CFG_EXCEPTION;
10983 token = read32 (ip + 1);
10984 klass = mini_get_class (method, token, generic_context);
10985 CHECK_TYPELOAD (klass);
10986 if (sp [0]->type != STACK_OBJ)
10989 context_used = mini_class_check_context_used (cfg, klass);
10991 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
10992 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
10993 MonoInst *args [3];
11000 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
11003 idx = get_castclass_cache_idx (cfg);
11004 args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
11006 *sp++ = mono_emit_method_call (cfg, mono_isinst, args, NULL);
11009 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
11010 MonoMethod *mono_isinst;
11011 MonoInst *iargs [1];
11014 mono_isinst = mono_marshal_get_isinst (klass);
11015 iargs [0] = sp [0];
11017 costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst),
11018 iargs, ip, cfg->real_offset, TRUE);
11019 CHECK_CFG_EXCEPTION;
11020 g_assert (costs > 0);
11023 cfg->real_offset += 5;
11027 inline_costs += costs;
11030 ins = handle_isinst (cfg, klass, *sp, context_used);
11031 CHECK_CFG_EXCEPTION;
11037 case CEE_UNBOX_ANY: {
11038 MonoInst *res, *addr;
11043 token = read32 (ip + 1);
11044 klass = mini_get_class (method, token, generic_context);
11045 CHECK_TYPELOAD (klass);
11047 mono_save_token_info (cfg, image, token, klass);
11049 context_used = mini_class_check_context_used (cfg, klass);
11051 if (mini_is_gsharedvt_klass (klass)) {
11052 res = handle_unbox_gsharedvt (cfg, klass, *sp);
11054 } else if (generic_class_is_reference_type (cfg, klass)) {
11055 res = handle_castclass (cfg, klass, *sp, ip, &inline_costs);
11056 CHECK_CFG_EXCEPTION;
11057 } else if (mono_class_is_nullable (klass)) {
11058 res = handle_unbox_nullable (cfg, *sp, klass, context_used);
11060 addr = handle_unbox (cfg, klass, sp, context_used);
11062 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11073 MonoClass *enum_class;
11074 MonoMethod *has_flag;
11080 token = read32 (ip + 1);
11081 klass = mini_get_class (method, token, generic_context);
11082 CHECK_TYPELOAD (klass);
11084 mono_save_token_info (cfg, image, token, klass);
11086 context_used = mini_class_check_context_used (cfg, klass);
11088 if (generic_class_is_reference_type (cfg, klass)) {
11094 if (klass == mono_defaults.void_class)
11096 if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
11098 /* frequent check in generic code: box (struct), brtrue */
11103 * <push int/long ptr>
11106 * constrained. MyFlags
11107 * callvirt instace bool class [mscorlib] System.Enum::HasFlag (class [mscorlib] System.Enum)
11109 * If we find this sequence and the operand types on box and constrained
11110 * are equal, we can emit a specialized instruction sequence instead of
11111 * the very slow HasFlag () call.
11113 if ((cfg->opt & MONO_OPT_INTRINS) &&
11114 /* Cheap checks first. */
11115 ip + 5 + 6 + 5 < end &&
11116 ip [5] == CEE_PREFIX1 &&
11117 ip [6] == CEE_CONSTRAINED_ &&
11118 ip [11] == CEE_CALLVIRT &&
11119 ip_in_bb (cfg, cfg->cbb, ip + 5 + 6 + 5) &&
11120 mono_class_is_enum (klass) &&
11121 (enum_class = mini_get_class (method, read32 (ip + 7), generic_context)) &&
11122 (has_flag = mini_get_method (cfg, method, read32 (ip + 12), NULL, generic_context)) &&
11123 has_flag->klass == mono_defaults.enum_class &&
11124 !strcmp (has_flag->name, "HasFlag") &&
11125 has_flag->signature->hasthis &&
11126 has_flag->signature->param_count == 1) {
11127 CHECK_TYPELOAD (enum_class);
11129 if (enum_class == klass) {
11130 MonoInst *enum_this, *enum_flag;
11135 enum_this = sp [0];
11136 enum_flag = sp [1];
11138 *sp++ = handle_enum_has_flag (cfg, klass, enum_this, enum_flag);
11143 // FIXME: LLVM can't handle the inconsistent bb linking
11144 if (!mono_class_is_nullable (klass) &&
11145 !mini_is_gsharedvt_klass (klass) &&
11146 ip + 5 < end && ip_in_bb (cfg, cfg->cbb, ip + 5) &&
11147 (ip [5] == CEE_BRTRUE ||
11148 ip [5] == CEE_BRTRUE_S ||
11149 ip [5] == CEE_BRFALSE ||
11150 ip [5] == CEE_BRFALSE_S)) {
11151 gboolean is_true = ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S;
11153 MonoBasicBlock *true_bb, *false_bb;
11157 if (cfg->verbose_level > 3) {
11158 printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
11159 printf ("<box+brtrue opt>\n");
11164 case CEE_BRFALSE_S:
11167 target = ip + 1 + (signed char)(*ip);
11174 target = ip + 4 + (gint)(read32 (ip));
11178 g_assert_not_reached ();
11182 * We need to link both bblocks, since it is needed for handling stack
11183 * arguments correctly (See test_0_box_brtrue_opt_regress_81102).
11184 * Branching to only one of them would lead to inconsistencies, so
11185 * generate an ICONST+BRTRUE, the branch opts will get rid of them.
11187 GET_BBLOCK (cfg, true_bb, target);
11188 GET_BBLOCK (cfg, false_bb, ip);
11190 mono_link_bblock (cfg, cfg->cbb, true_bb);
11191 mono_link_bblock (cfg, cfg->cbb, false_bb);
11193 if (sp != stack_start) {
11194 handle_stack_args (cfg, stack_start, sp - stack_start);
11196 CHECK_UNVERIFIABLE (cfg);
11199 if (COMPILE_LLVM (cfg)) {
11200 dreg = alloc_ireg (cfg);
11201 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
11202 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, dreg, is_true ? 0 : 1);
11204 MONO_EMIT_NEW_BRANCH_BLOCK2 (cfg, OP_IBEQ, true_bb, false_bb);
11206 /* The JIT can't eliminate the iconst+compare */
11207 MONO_INST_NEW (cfg, ins, OP_BR);
11208 ins->inst_target_bb = is_true ? true_bb : false_bb;
11209 MONO_ADD_INS (cfg->cbb, ins);
11212 start_new_bblock = 1;
11216 *sp++ = handle_box (cfg, val, klass, context_used);
11218 CHECK_CFG_EXCEPTION;
11227 token = read32 (ip + 1);
11228 klass = mini_get_class (method, token, generic_context);
11229 CHECK_TYPELOAD (klass);
11231 mono_save_token_info (cfg, image, token, klass);
11233 context_used = mini_class_check_context_used (cfg, klass);
11235 if (mono_class_is_nullable (klass)) {
11238 val = handle_unbox_nullable (cfg, *sp, klass, context_used);
11239 EMIT_NEW_VARLOADA (cfg, ins, get_vreg_to_inst (cfg, val->dreg), &val->klass->byval_arg);
11243 ins = handle_unbox (cfg, klass, sp, context_used);
11256 MonoClassField *field;
11257 #ifndef DISABLE_REMOTING
11261 gboolean is_instance;
11263 gpointer addr = NULL;
11264 gboolean is_special_static;
11266 MonoInst *store_val = NULL;
11267 MonoInst *thread_ins;
11270 is_instance = (op == CEE_LDFLD || op == CEE_LDFLDA || op == CEE_STFLD);
11272 if (op == CEE_STFLD) {
11275 store_val = sp [1];
11280 if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
11282 if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
11285 if (op == CEE_STSFLD) {
11288 store_val = sp [0];
11293 token = read32 (ip + 1);
11294 if (method->wrapper_type != MONO_WRAPPER_NONE) {
11295 field = (MonoClassField *)mono_method_get_wrapper_data (method, token);
11296 klass = field->parent;
11299 field = mono_field_from_token_checked (image, token, &klass, generic_context, &cfg->error);
11302 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
11303 FIELD_ACCESS_FAILURE (method, field);
11304 mono_class_init (klass);
11306 /* if the class is Critical then transparent code cannot access it's fields */
11307 if (!is_instance && mono_security_core_clr_enabled ())
11308 ensure_method_is_allowed_to_access_field (cfg, method, field);
11310 /* XXX this is technically required but, so far (SL2), no [SecurityCritical] types (not many exists) have
11311 any visible *instance* field (in fact there's a single case for a static field in Marshal) XXX
11312 if (mono_security_core_clr_enabled ())
11313 ensure_method_is_allowed_to_access_field (cfg, method, field);
11316 ftype = mono_field_get_type (field);
11319 * LDFLD etc. is usable on static fields as well, so convert those cases to
11322 if (is_instance && ftype->attrs & FIELD_ATTRIBUTE_STATIC) {
11334 g_assert_not_reached ();
11336 is_instance = FALSE;
11339 context_used = mini_class_check_context_used (cfg, klass);
11341 /* INSTANCE CASE */
11343 foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
11344 if (op == CEE_STFLD) {
11345 if (target_type_is_incompatible (cfg, field->type, sp [1]))
11347 #ifndef DISABLE_REMOTING
11348 if ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
11349 MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
11350 MonoInst *iargs [5];
11352 GSHAREDVT_FAILURE (op);
11354 iargs [0] = sp [0];
11355 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11356 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
11357 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) :
11359 iargs [4] = sp [1];
11361 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
11362 costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
11363 iargs, ip, cfg->real_offset, TRUE);
11364 CHECK_CFG_EXCEPTION;
11365 g_assert (costs > 0);
11367 cfg->real_offset += 5;
11369 inline_costs += costs;
11371 mono_emit_method_call (cfg, stfld_wrapper, iargs, NULL);
11376 MonoInst *store, *wbarrier_ptr_ins = NULL;
11378 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
11380 if (mini_is_gsharedvt_klass (klass)) {
11381 MonoInst *offset_ins;
11383 context_used = mini_class_check_context_used (cfg, klass);
11385 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11386 /* The value is offset by 1 */
11387 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11388 dreg = alloc_ireg_mp (cfg);
11389 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11390 wbarrier_ptr_ins = ins;
11391 /* The decomposition will call mini_emit_stobj () which will emit a wbarrier if needed */
11392 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, dreg, 0, sp [1]->dreg);
11394 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
11396 if (sp [0]->opcode != OP_LDADDR)
11397 store->flags |= MONO_INST_FAULT;
11399 if (cfg->gen_write_barriers && mini_type_to_stind (cfg, field->type) == CEE_STIND_REF && !(sp [1]->opcode == OP_PCONST && sp [1]->inst_c0 == 0)) {
11400 if (mini_is_gsharedvt_klass (klass)) {
11401 g_assert (wbarrier_ptr_ins);
11402 emit_write_barrier (cfg, wbarrier_ptr_ins, sp [1]);
11404 /* insert call to write barrier */
11408 dreg = alloc_ireg_mp (cfg);
11409 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
11410 emit_write_barrier (cfg, ptr, sp [1]);
11414 store->flags |= ins_flag;
11421 #ifndef DISABLE_REMOTING
11422 if (is_instance && ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class)) {
11423 MonoMethod *wrapper = (op == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
11424 MonoInst *iargs [4];
11426 GSHAREDVT_FAILURE (op);
11428 iargs [0] = sp [0];
11429 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11430 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
11431 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
11432 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
11433 costs = inline_method (cfg, wrapper, mono_method_signature (wrapper),
11434 iargs, ip, cfg->real_offset, TRUE);
11435 CHECK_CFG_EXCEPTION;
11436 g_assert (costs > 0);
11438 cfg->real_offset += 5;
11442 inline_costs += costs;
11444 ins = mono_emit_method_call (cfg, wrapper, iargs, NULL);
11450 if (sp [0]->type == STACK_VTYPE) {
11453 /* Have to compute the address of the variable */
11455 var = get_vreg_to_inst (cfg, sp [0]->dreg);
11457 var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, sp [0]->dreg);
11459 g_assert (var->klass == klass);
11461 EMIT_NEW_VARLOADA (cfg, ins, var, &var->klass->byval_arg);
11465 if (op == CEE_LDFLDA) {
11466 if (sp [0]->type == STACK_OBJ) {
11467 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, sp [0]->dreg, 0);
11468 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
11471 dreg = alloc_ireg_mp (cfg);
11473 if (mini_is_gsharedvt_klass (klass)) {
11474 MonoInst *offset_ins;
11476 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11477 /* The value is offset by 1 */
11478 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11479 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11481 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
11483 ins->klass = mono_class_from_mono_type (field->type);
11484 ins->type = STACK_MP;
11489 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
11491 if (mini_is_gsharedvt_klass (klass)) {
11492 MonoInst *offset_ins;
11494 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11495 /* The value is offset by 1 */
11496 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11497 dreg = alloc_ireg_mp (cfg);
11498 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11499 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, dreg, 0);
11501 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset);
11503 load->flags |= ins_flag;
11504 if (sp [0]->opcode != OP_LDADDR)
11505 load->flags |= MONO_INST_FAULT;
11517 context_used = mini_class_check_context_used (cfg, klass);
11519 if (ftype->attrs & FIELD_ATTRIBUTE_LITERAL) {
11520 mono_error_set_field_load (&cfg->error, field->parent, field->name, "Using static instructions with literal field");
11524 /* The special_static_fields field is init'd in mono_class_vtable, so it needs
11525 * to be called here.
11527 if (!context_used && !(cfg->opt & MONO_OPT_SHARED)) {
11528 mono_class_vtable (cfg->domain, klass);
11529 CHECK_TYPELOAD (klass);
11531 mono_domain_lock (cfg->domain);
11532 if (cfg->domain->special_static_fields)
11533 addr = g_hash_table_lookup (cfg->domain->special_static_fields, field);
11534 mono_domain_unlock (cfg->domain);
11536 is_special_static = mono_class_field_is_special_static (field);
11538 if (is_special_static && ((gsize)addr & 0x80000000) == 0)
11539 thread_ins = mono_get_thread_intrinsic (cfg);
11543 /* Generate IR to compute the field address */
11544 if (is_special_static && ((gsize)addr & 0x80000000) == 0 && thread_ins && !(cfg->opt & MONO_OPT_SHARED) && !context_used) {
11546 * Fast access to TLS data
11547 * Inline version of get_thread_static_data () in
11551 int idx, static_data_reg, array_reg, dreg;
11553 GSHAREDVT_FAILURE (op);
11555 MONO_ADD_INS (cfg->cbb, thread_ins);
11556 static_data_reg = alloc_ireg (cfg);
11557 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
11559 if (cfg->compile_aot) {
11560 int offset_reg, offset2_reg, idx_reg;
11562 /* For TLS variables, this will return the TLS offset */
11563 EMIT_NEW_SFLDACONST (cfg, ins, field);
11564 offset_reg = ins->dreg;
11565 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset_reg, offset_reg, 0x7fffffff);
11566 idx_reg = alloc_ireg (cfg);
11567 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, idx_reg, offset_reg, 0x3f);
11568 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
11569 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
11570 array_reg = alloc_ireg (cfg);
11571 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
11572 offset2_reg = alloc_ireg (cfg);
11573 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_UN_IMM, offset2_reg, offset_reg, 6);
11574 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset2_reg, 0x1ffffff);
11575 dreg = alloc_ireg (cfg);
11576 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, array_reg, offset2_reg);
11578 offset = (gsize)addr & 0x7fffffff;
11579 idx = offset & 0x3f;
11581 array_reg = alloc_ireg (cfg);
11582 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, idx * sizeof (gpointer));
11583 dreg = alloc_ireg (cfg);
11584 EMIT_NEW_BIALU_IMM (cfg, ins, OP_ADD_IMM, dreg, array_reg, ((offset >> 6) & 0x1ffffff));
11586 } else if ((cfg->opt & MONO_OPT_SHARED) ||
11587 (cfg->compile_aot && is_special_static) ||
11588 (context_used && is_special_static)) {
11589 MonoInst *iargs [2];
11591 g_assert (field->parent);
11592 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11593 if (context_used) {
11594 iargs [1] = emit_get_rgctx_field (cfg, context_used,
11595 field, MONO_RGCTX_INFO_CLASS_FIELD);
11597 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11599 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11600 } else if (context_used) {
11601 MonoInst *static_data;
11604 g_print ("sharing static field access in %s.%s.%s - depth %d offset %d\n",
11605 method->klass->name_space, method->klass->name, method->name,
11606 depth, field->offset);
11609 if (mono_class_needs_cctor_run (klass, method))
11610 emit_class_init (cfg, klass);
11613 * The pointer we're computing here is
11615 * super_info.static_data + field->offset
11617 static_data = emit_get_rgctx_klass (cfg, context_used,
11618 klass, MONO_RGCTX_INFO_STATIC_DATA);
11620 if (mini_is_gsharedvt_klass (klass)) {
11621 MonoInst *offset_ins;
11623 offset_ins = emit_get_rgctx_field (cfg, context_used, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11624 /* The value is offset by 1 */
11625 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11626 dreg = alloc_ireg_mp (cfg);
11627 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, static_data->dreg, offset_ins->dreg);
11628 } else if (field->offset == 0) {
11631 int addr_reg = mono_alloc_preg (cfg);
11632 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, addr_reg, static_data->dreg, field->offset);
11634 } else if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) {
11635 MonoInst *iargs [2];
11637 g_assert (field->parent);
11638 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11639 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11640 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11642 MonoVTable *vtable = NULL;
11644 if (!cfg->compile_aot)
11645 vtable = mono_class_vtable (cfg->domain, klass);
11646 CHECK_TYPELOAD (klass);
11649 if (mini_field_access_needs_cctor_run (cfg, method, klass, vtable)) {
11650 if (!(g_slist_find (class_inits, klass))) {
11651 emit_class_init (cfg, klass);
11652 if (cfg->verbose_level > 2)
11653 printf ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, mono_field_get_name (field));
11654 class_inits = g_slist_prepend (class_inits, klass);
11657 if (cfg->run_cctors) {
11658 /* This makes so that inline cannot trigger */
11659 /* .cctors: too many apps depend on them */
11660 /* running with a specific order... */
11662 if (! vtable->initialized)
11663 INLINE_FAILURE ("class init");
11664 if (!mono_runtime_class_init_full (vtable, &cfg->error)) {
11665 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
11666 goto exception_exit;
11670 if (cfg->compile_aot)
11671 EMIT_NEW_SFLDACONST (cfg, ins, field);
11674 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11676 EMIT_NEW_PCONST (cfg, ins, addr);
11679 MonoInst *iargs [1];
11680 EMIT_NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
11681 ins = mono_emit_jit_icall (cfg, mono_get_special_static_data, iargs);
11685 /* Generate IR to do the actual load/store operation */
11687 if ((op == CEE_STFLD || op == CEE_STSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11688 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11689 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11692 if (op == CEE_LDSFLDA) {
11693 ins->klass = mono_class_from_mono_type (ftype);
11694 ins->type = STACK_PTR;
11696 } else if (op == CEE_STSFLD) {
11699 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, ftype, ins->dreg, 0, store_val->dreg);
11700 store->flags |= ins_flag;
11702 gboolean is_const = FALSE;
11703 MonoVTable *vtable = NULL;
11704 gpointer addr = NULL;
11706 if (!context_used) {
11707 vtable = mono_class_vtable (cfg->domain, klass);
11708 CHECK_TYPELOAD (klass);
11710 if ((ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && (((addr = mono_aot_readonly_field_override (field)) != NULL) ||
11711 (!context_used && !((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) && vtable->initialized))) {
11712 int ro_type = ftype->type;
11714 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11715 if (ro_type == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) {
11716 ro_type = mono_class_enum_basetype (ftype->data.klass)->type;
11719 GSHAREDVT_FAILURE (op);
11721 /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/
11724 case MONO_TYPE_BOOLEAN:
11726 EMIT_NEW_ICONST (cfg, *sp, *((guint8 *)addr));
11730 EMIT_NEW_ICONST (cfg, *sp, *((gint8 *)addr));
11733 case MONO_TYPE_CHAR:
11735 EMIT_NEW_ICONST (cfg, *sp, *((guint16 *)addr));
11739 EMIT_NEW_ICONST (cfg, *sp, *((gint16 *)addr));
11744 EMIT_NEW_ICONST (cfg, *sp, *((gint32 *)addr));
11748 EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr));
11753 case MONO_TYPE_PTR:
11754 case MONO_TYPE_FNPTR:
11755 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11756 type_to_eval_stack_type ((cfg), field->type, *sp);
11759 case MONO_TYPE_STRING:
11760 case MONO_TYPE_OBJECT:
11761 case MONO_TYPE_CLASS:
11762 case MONO_TYPE_SZARRAY:
11763 case MONO_TYPE_ARRAY:
11764 if (!mono_gc_is_moving ()) {
11765 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11766 type_to_eval_stack_type ((cfg), field->type, *sp);
11774 EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr));
11779 case MONO_TYPE_VALUETYPE:
11789 CHECK_STACK_OVF (1);
11791 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, ins->dreg, 0);
11792 load->flags |= ins_flag;
11798 if ((op == CEE_LDFLD || op == CEE_LDSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11799 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
11800 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
11811 token = read32 (ip + 1);
11812 klass = mini_get_class (method, token, generic_context);
11813 CHECK_TYPELOAD (klass);
11814 if (ins_flag & MONO_INST_VOLATILE) {
11815 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11816 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11818 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
11819 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0, sp [1]->dreg);
11820 ins->flags |= ins_flag;
11821 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
11822 generic_class_is_reference_type (cfg, klass)) {
11823 /* insert call to write barrier */
11824 emit_write_barrier (cfg, sp [0], sp [1]);
11836 const char *data_ptr;
11838 guint32 field_token;
11844 token = read32 (ip + 1);
11846 klass = mini_get_class (method, token, generic_context);
11847 CHECK_TYPELOAD (klass);
11849 context_used = mini_class_check_context_used (cfg, klass);
11851 if (sp [0]->type == STACK_I8 || (SIZEOF_VOID_P == 8 && sp [0]->type == STACK_PTR)) {
11852 MONO_INST_NEW (cfg, ins, OP_LCONV_TO_OVF_U4);
11853 ins->sreg1 = sp [0]->dreg;
11854 ins->type = STACK_I4;
11855 ins->dreg = alloc_ireg (cfg);
11856 MONO_ADD_INS (cfg->cbb, ins);
11857 *sp = mono_decompose_opcode (cfg, ins);
11860 if (context_used) {
11861 MonoInst *args [3];
11862 MonoClass *array_class = mono_array_class_get (klass, 1);
11863 MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class);
11865 /* FIXME: Use OP_NEWARR and decompose later to help abcrem */
11868 args [0] = emit_get_rgctx_klass (cfg, context_used,
11869 array_class, MONO_RGCTX_INFO_VTABLE);
11874 ins = mono_emit_method_call (cfg, managed_alloc, args, NULL);
11876 ins = mono_emit_jit_icall (cfg, ves_icall_array_new_specific, args);
11878 if (cfg->opt & MONO_OPT_SHARED) {
11879 /* Decompose now to avoid problems with references to the domainvar */
11880 MonoInst *iargs [3];
11882 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11883 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11884 iargs [2] = sp [0];
11886 ins = mono_emit_jit_icall (cfg, ves_icall_array_new, iargs);
11888 /* Decompose later since it is needed by abcrem */
11889 MonoClass *array_type = mono_array_class_get (klass, 1);
11890 mono_class_vtable (cfg->domain, array_type);
11891 CHECK_TYPELOAD (array_type);
11893 MONO_INST_NEW (cfg, ins, OP_NEWARR);
11894 ins->dreg = alloc_ireg_ref (cfg);
11895 ins->sreg1 = sp [0]->dreg;
11896 ins->inst_newa_class = klass;
11897 ins->type = STACK_OBJ;
11898 ins->klass = array_type;
11899 MONO_ADD_INS (cfg->cbb, ins);
11900 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11901 cfg->cbb->has_array_access = TRUE;
11903 /* Needed so mono_emit_load_get_addr () gets called */
11904 mono_get_got_var (cfg);
11914 * we inline/optimize the initialization sequence if possible.
11915 * we should also allocate the array as not cleared, since we spend as much time clearing to 0 as initializing
11916 * for small sizes open code the memcpy
11917 * ensure the rva field is big enough
11919 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))) {
11920 MonoMethod *memcpy_method = get_memcpy_method ();
11921 MonoInst *iargs [3];
11922 int add_reg = alloc_ireg_mp (cfg);
11924 EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, add_reg, ins->dreg, MONO_STRUCT_OFFSET (MonoArray, vector));
11925 if (cfg->compile_aot) {
11926 EMIT_NEW_AOTCONST_TOKEN (cfg, iargs [1], MONO_PATCH_INFO_RVA, method->klass->image, GPOINTER_TO_UINT(field_token), STACK_PTR, NULL);
11928 EMIT_NEW_PCONST (cfg, iargs [1], (char*)data_ptr);
11930 EMIT_NEW_ICONST (cfg, iargs [2], data_size);
11931 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
11940 if (sp [0]->type != STACK_OBJ)
11943 MONO_INST_NEW (cfg, ins, OP_LDLEN);
11944 ins->dreg = alloc_preg (cfg);
11945 ins->sreg1 = sp [0]->dreg;
11946 ins->type = STACK_I4;
11947 /* This flag will be inherited by the decomposition */
11948 ins->flags |= MONO_INST_FAULT;
11949 MONO_ADD_INS (cfg->cbb, ins);
11950 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11951 cfg->cbb->has_array_access = TRUE;
11959 if (sp [0]->type != STACK_OBJ)
11962 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11964 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11965 CHECK_TYPELOAD (klass);
11966 /* we need to make sure that this array is exactly the type it needs
11967 * to be for correctness. the wrappers are lax with their usage
11968 * so we need to ignore them here
11970 if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
11971 MonoClass *array_class = mono_array_class_get (klass, 1);
11972 mini_emit_check_array_type (cfg, sp [0], array_class);
11973 CHECK_TYPELOAD (array_class);
11977 ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11982 case CEE_LDELEM_I1:
11983 case CEE_LDELEM_U1:
11984 case CEE_LDELEM_I2:
11985 case CEE_LDELEM_U2:
11986 case CEE_LDELEM_I4:
11987 case CEE_LDELEM_U4:
11988 case CEE_LDELEM_I8:
11990 case CEE_LDELEM_R4:
11991 case CEE_LDELEM_R8:
11992 case CEE_LDELEM_REF: {
11998 if (*ip == CEE_LDELEM) {
12000 token = read32 (ip + 1);
12001 klass = mini_get_class (method, token, generic_context);
12002 CHECK_TYPELOAD (klass);
12003 mono_class_init (klass);
12006 klass = array_access_to_klass (*ip);
12008 if (sp [0]->type != STACK_OBJ)
12011 cfg->flags |= MONO_CFG_HAS_LDELEMA;
12013 if (mini_is_gsharedvt_variable_klass (klass)) {
12014 // FIXME-VT: OP_ICONST optimization
12015 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
12016 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
12017 ins->opcode = OP_LOADV_MEMBASE;
12018 } else if (sp [1]->opcode == OP_ICONST) {
12019 int array_reg = sp [0]->dreg;
12020 int index_reg = sp [1]->dreg;
12021 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
12023 if (SIZEOF_REGISTER == 8 && COMPILE_LLVM (cfg))
12024 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, index_reg);
12026 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
12027 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset);
12029 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
12030 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
12033 if (*ip == CEE_LDELEM)
12040 case CEE_STELEM_I1:
12041 case CEE_STELEM_I2:
12042 case CEE_STELEM_I4:
12043 case CEE_STELEM_I8:
12044 case CEE_STELEM_R4:
12045 case CEE_STELEM_R8:
12046 case CEE_STELEM_REF:
12051 cfg->flags |= MONO_CFG_HAS_LDELEMA;
12053 if (*ip == CEE_STELEM) {
12055 token = read32 (ip + 1);
12056 klass = mini_get_class (method, token, generic_context);
12057 CHECK_TYPELOAD (klass);
12058 mono_class_init (klass);
12061 klass = array_access_to_klass (*ip);
12063 if (sp [0]->type != STACK_OBJ)
12066 emit_array_store (cfg, klass, sp, TRUE);
12068 if (*ip == CEE_STELEM)
12075 case CEE_CKFINITE: {
12079 if (cfg->llvm_only) {
12080 MonoInst *iargs [1];
12082 iargs [0] = sp [0];
12083 *sp++ = mono_emit_jit_icall (cfg, mono_ckfinite, iargs);
12085 MONO_INST_NEW (cfg, ins, OP_CKFINITE);
12086 ins->sreg1 = sp [0]->dreg;
12087 ins->dreg = alloc_freg (cfg);
12088 ins->type = STACK_R8;
12089 MONO_ADD_INS (cfg->cbb, ins);
12091 *sp++ = mono_decompose_opcode (cfg, ins);
12097 case CEE_REFANYVAL: {
12098 MonoInst *src_var, *src;
12100 int klass_reg = alloc_preg (cfg);
12101 int dreg = alloc_preg (cfg);
12103 GSHAREDVT_FAILURE (*ip);
12106 MONO_INST_NEW (cfg, ins, *ip);
12109 klass = mini_get_class (method, read32 (ip + 1), generic_context);
12110 CHECK_TYPELOAD (klass);
12112 context_used = mini_class_check_context_used (cfg, klass);
12115 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12117 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
12118 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
12119 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass));
12121 if (context_used) {
12122 MonoInst *klass_ins;
12124 klass_ins = emit_get_rgctx_klass (cfg, context_used,
12125 klass, MONO_RGCTX_INFO_KLASS);
12128 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_ins->dreg);
12129 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
12131 mini_emit_class_check (cfg, klass_reg, klass);
12133 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value));
12134 ins->type = STACK_MP;
12135 ins->klass = klass;
12140 case CEE_MKREFANY: {
12141 MonoInst *loc, *addr;
12143 GSHAREDVT_FAILURE (*ip);
12146 MONO_INST_NEW (cfg, ins, *ip);
12149 klass = mini_get_class (method, read32 (ip + 1), generic_context);
12150 CHECK_TYPELOAD (klass);
12152 context_used = mini_class_check_context_used (cfg, klass);
12154 loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
12155 EMIT_NEW_TEMPLOADA (cfg, addr, loc->inst_c0);
12157 if (context_used) {
12158 MonoInst *const_ins;
12159 int type_reg = alloc_preg (cfg);
12161 const_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
12162 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg);
12163 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
12164 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
12165 } else if (cfg->compile_aot) {
12166 int const_reg = alloc_preg (cfg);
12167 int type_reg = alloc_preg (cfg);
12169 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
12170 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_reg);
12171 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_reg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
12172 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
12174 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), &klass->byval_arg);
12175 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), klass);
12177 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value), sp [0]->dreg);
12179 EMIT_NEW_TEMPLOAD (cfg, ins, loc->inst_c0);
12180 ins->type = STACK_VTYPE;
12181 ins->klass = mono_defaults.typed_reference_class;
12186 case CEE_LDTOKEN: {
12188 MonoClass *handle_class;
12190 CHECK_STACK_OVF (1);
12193 n = read32 (ip + 1);
12195 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD ||
12196 method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
12197 handle = mono_method_get_wrapper_data (method, n);
12198 handle_class = (MonoClass *)mono_method_get_wrapper_data (method, n + 1);
12199 if (handle_class == mono_defaults.typehandle_class)
12200 handle = &((MonoClass*)handle)->byval_arg;
12203 handle = mono_ldtoken_checked (image, n, &handle_class, generic_context, &cfg->error);
12208 mono_class_init (handle_class);
12209 if (cfg->gshared) {
12210 if (mono_metadata_token_table (n) == MONO_TABLE_TYPEDEF ||
12211 mono_metadata_token_table (n) == MONO_TABLE_TYPEREF) {
12212 /* This case handles ldtoken
12213 of an open type, like for
12216 } else if (handle_class == mono_defaults.typehandle_class) {
12217 context_used = mini_class_check_context_used (cfg, mono_class_from_mono_type ((MonoType *)handle));
12218 } else if (handle_class == mono_defaults.fieldhandle_class)
12219 context_used = mini_class_check_context_used (cfg, ((MonoClassField*)handle)->parent);
12220 else if (handle_class == mono_defaults.methodhandle_class)
12221 context_used = mini_method_check_context_used (cfg, (MonoMethod *)handle);
12223 g_assert_not_reached ();
12226 if ((cfg->opt & MONO_OPT_SHARED) &&
12227 method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD &&
12228 method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) {
12229 MonoInst *addr, *vtvar, *iargs [3];
12230 int method_context_used;
12232 method_context_used = mini_method_check_context_used (cfg, method);
12234 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
12236 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
12237 EMIT_NEW_ICONST (cfg, iargs [1], n);
12238 if (method_context_used) {
12239 iargs [2] = emit_get_rgctx_method (cfg, method_context_used,
12240 method, MONO_RGCTX_INFO_METHOD);
12241 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper_generic_shared, iargs);
12243 EMIT_NEW_PCONST (cfg, iargs [2], generic_context);
12244 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper, iargs);
12246 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12248 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
12250 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12252 if ((ip + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 5) &&
12253 ((ip [5] == CEE_CALL) || (ip [5] == CEE_CALLVIRT)) &&
12254 (cmethod = mini_get_method (cfg, method, read32 (ip + 6), NULL, generic_context)) &&
12255 (cmethod->klass == mono_defaults.systemtype_class) &&
12256 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
12257 MonoClass *tclass = mono_class_from_mono_type ((MonoType *)handle);
12259 mono_class_init (tclass);
12260 if (context_used) {
12261 ins = emit_get_rgctx_klass (cfg, context_used,
12262 tclass, MONO_RGCTX_INFO_REFLECTION_TYPE);
12263 } else if (cfg->compile_aot) {
12264 if (method->wrapper_type) {
12265 mono_error_init (&error); //got to do it since there are multiple conditionals below
12266 if (mono_class_get_checked (tclass->image, tclass->type_token, &error) == tclass && !generic_context) {
12267 /* Special case for static synchronized wrappers */
12268 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, tclass->image, tclass->type_token, generic_context);
12270 mono_error_cleanup (&error); /* FIXME don't swallow the error */
12271 /* FIXME: n is not a normal token */
12273 EMIT_NEW_PCONST (cfg, ins, NULL);
12276 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n, generic_context);
12279 MonoReflectionType *rt = mono_type_get_object_checked (cfg->domain, (MonoType *)handle, &cfg->error);
12281 EMIT_NEW_PCONST (cfg, ins, rt);
12283 ins->type = STACK_OBJ;
12284 ins->klass = cmethod->klass;
12287 MonoInst *addr, *vtvar;
12289 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
12291 if (context_used) {
12292 if (handle_class == mono_defaults.typehandle_class) {
12293 ins = emit_get_rgctx_klass (cfg, context_used,
12294 mono_class_from_mono_type ((MonoType *)handle),
12295 MONO_RGCTX_INFO_TYPE);
12296 } else if (handle_class == mono_defaults.methodhandle_class) {
12297 ins = emit_get_rgctx_method (cfg, context_used,
12298 (MonoMethod *)handle, MONO_RGCTX_INFO_METHOD);
12299 } else if (handle_class == mono_defaults.fieldhandle_class) {
12300 ins = emit_get_rgctx_field (cfg, context_used,
12301 (MonoClassField *)handle, MONO_RGCTX_INFO_CLASS_FIELD);
12303 g_assert_not_reached ();
12305 } else if (cfg->compile_aot) {
12306 EMIT_NEW_LDTOKENCONST (cfg, ins, image, n, generic_context);
12308 EMIT_NEW_PCONST (cfg, ins, handle);
12310 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12311 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
12312 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12322 MONO_INST_NEW (cfg, ins, OP_THROW);
12324 ins->sreg1 = sp [0]->dreg;
12326 cfg->cbb->out_of_line = TRUE;
12327 MONO_ADD_INS (cfg->cbb, ins);
12328 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
12329 MONO_ADD_INS (cfg->cbb, ins);
12332 link_bblock (cfg, cfg->cbb, end_bblock);
12333 start_new_bblock = 1;
12334 /* This can complicate code generation for llvm since the return value might not be defined */
12335 if (COMPILE_LLVM (cfg))
12336 INLINE_FAILURE ("throw");
12338 case CEE_ENDFINALLY:
12339 /* mono_save_seq_point_info () depends on this */
12340 if (sp != stack_start)
12341 emit_seq_point (cfg, method, ip, FALSE, FALSE);
12342 MONO_INST_NEW (cfg, ins, OP_ENDFINALLY);
12343 MONO_ADD_INS (cfg->cbb, ins);
12345 start_new_bblock = 1;
12348 * Control will leave the method so empty the stack, otherwise
12349 * the next basic block will start with a nonempty stack.
12351 while (sp != stack_start) {
12356 case CEE_LEAVE_S: {
12359 if (*ip == CEE_LEAVE) {
12361 target = ip + 5 + (gint32)read32(ip + 1);
12364 target = ip + 2 + (signed char)(ip [1]);
12367 /* empty the stack */
12368 while (sp != stack_start) {
12373 * If this leave statement is in a catch block, check for a
12374 * pending exception, and rethrow it if necessary.
12375 * We avoid doing this in runtime invoke wrappers, since those are called
12376 * by native code which excepts the wrapper to catch all exceptions.
12378 for (i = 0; i < header->num_clauses; ++i) {
12379 MonoExceptionClause *clause = &header->clauses [i];
12382 * Use <= in the final comparison to handle clauses with multiple
12383 * leave statements, like in bug #78024.
12384 * The ordering of the exception clauses guarantees that we find the
12385 * innermost clause.
12387 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) {
12389 MonoBasicBlock *dont_throw;
12394 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0);
12397 exc_ins = mono_emit_jit_icall (cfg, mono_thread_get_undeniable_exception, NULL);
12399 NEW_BBLOCK (cfg, dont_throw);
12402 * Currently, we always rethrow the abort exception, despite the
12403 * fact that this is not correct. See thread6.cs for an example.
12404 * But propagating the abort exception is more important than
12405 * getting the sematics right.
12407 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, exc_ins->dreg, 0);
12408 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
12409 MONO_EMIT_NEW_UNALU (cfg, OP_THROW, -1, exc_ins->dreg);
12411 MONO_START_BB (cfg, dont_throw);
12416 cfg->cbb->try_end = (intptr_t)(ip - header->code);
12419 if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
12421 MonoExceptionClause *clause;
12423 for (tmp = handlers; tmp; tmp = tmp->next) {
12424 clause = (MonoExceptionClause *)tmp->data;
12425 tblock = cfg->cil_offset_to_bb [clause->handler_offset];
12427 link_bblock (cfg, cfg->cbb, tblock);
12428 MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
12429 ins->inst_target_bb = tblock;
12430 ins->inst_eh_block = clause;
12431 MONO_ADD_INS (cfg->cbb, ins);
12432 cfg->cbb->has_call_handler = 1;
12433 if (COMPILE_LLVM (cfg)) {
12434 MonoBasicBlock *target_bb;
12437 * Link the finally bblock with the target, since it will
12438 * conceptually branch there.
12440 GET_BBLOCK (cfg, tblock, cfg->cil_start + clause->handler_offset + clause->handler_len - 1);
12441 GET_BBLOCK (cfg, target_bb, target);
12442 link_bblock (cfg, tblock, target_bb);
12445 g_list_free (handlers);
12448 MONO_INST_NEW (cfg, ins, OP_BR);
12449 MONO_ADD_INS (cfg->cbb, ins);
12450 GET_BBLOCK (cfg, tblock, target);
12451 link_bblock (cfg, cfg->cbb, tblock);
12452 ins->inst_target_bb = tblock;
12454 start_new_bblock = 1;
12456 if (*ip == CEE_LEAVE)
12465 * Mono specific opcodes
12467 case MONO_CUSTOM_PREFIX: {
12469 g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
12473 case CEE_MONO_ICALL: {
12475 MonoJitICallInfo *info;
12477 token = read32 (ip + 2);
12478 func = mono_method_get_wrapper_data (method, token);
12479 info = mono_find_jit_icall_by_addr (func);
12481 g_error ("Could not find icall address in wrapper %s", mono_method_full_name (method, 1));
12484 CHECK_STACK (info->sig->param_count);
12485 sp -= info->sig->param_count;
12487 ins = mono_emit_jit_icall (cfg, info->func, sp);
12488 if (!MONO_TYPE_IS_VOID (info->sig->ret))
12492 inline_costs += 10 * num_calls++;
12496 case CEE_MONO_LDPTR_CARD_TABLE:
12497 case CEE_MONO_LDPTR_NURSERY_START:
12498 case CEE_MONO_LDPTR_NURSERY_BITS:
12499 case CEE_MONO_LDPTR_INT_REQ_FLAG: {
12500 CHECK_STACK_OVF (1);
12503 case CEE_MONO_LDPTR_CARD_TABLE:
12504 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
12506 case CEE_MONO_LDPTR_NURSERY_START:
12507 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_NURSERY_START, NULL);
12509 case CEE_MONO_LDPTR_NURSERY_BITS:
12510 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_NURSERY_BITS, NULL);
12512 case CEE_MONO_LDPTR_INT_REQ_FLAG:
12513 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
12519 inline_costs += 10 * num_calls++;
12522 case CEE_MONO_LDPTR: {
12525 CHECK_STACK_OVF (1);
12527 token = read32 (ip + 2);
12529 ptr = mono_method_get_wrapper_data (method, token);
12530 EMIT_NEW_PCONST (cfg, ins, ptr);
12533 inline_costs += 10 * num_calls++;
12534 /* Can't embed random pointers into AOT code */
12538 case CEE_MONO_JIT_ICALL_ADDR: {
12539 MonoJitICallInfo *callinfo;
12542 CHECK_STACK_OVF (1);
12544 token = read32 (ip + 2);
12546 ptr = mono_method_get_wrapper_data (method, token);
12547 callinfo = mono_find_jit_icall_by_addr (ptr);
12548 g_assert (callinfo);
12549 EMIT_NEW_JIT_ICALL_ADDRCONST (cfg, ins, (char*)callinfo->name);
12552 inline_costs += 10 * num_calls++;
12555 case CEE_MONO_ICALL_ADDR: {
12556 MonoMethod *cmethod;
12559 CHECK_STACK_OVF (1);
12561 token = read32 (ip + 2);
12563 cmethod = (MonoMethod *)mono_method_get_wrapper_data (method, token);
12565 if (cfg->compile_aot) {
12566 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, cmethod);
12568 ptr = mono_lookup_internal_call (cmethod);
12570 EMIT_NEW_PCONST (cfg, ins, ptr);
12576 case CEE_MONO_VTADDR: {
12577 MonoInst *src_var, *src;
12583 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12584 EMIT_NEW_VARLOADA ((cfg), (src), src_var, src_var->inst_vtype);
12589 case CEE_MONO_NEWOBJ: {
12590 MonoInst *iargs [2];
12592 CHECK_STACK_OVF (1);
12594 token = read32 (ip + 2);
12595 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12596 mono_class_init (klass);
12597 NEW_DOMAINCONST (cfg, iargs [0]);
12598 MONO_ADD_INS (cfg->cbb, iargs [0]);
12599 NEW_CLASSCONST (cfg, iargs [1], klass);
12600 MONO_ADD_INS (cfg->cbb, iargs [1]);
12601 *sp++ = mono_emit_jit_icall (cfg, ves_icall_object_new, iargs);
12603 inline_costs += 10 * num_calls++;
12606 case CEE_MONO_OBJADDR:
12609 MONO_INST_NEW (cfg, ins, OP_MOVE);
12610 ins->dreg = alloc_ireg_mp (cfg);
12611 ins->sreg1 = sp [0]->dreg;
12612 ins->type = STACK_MP;
12613 MONO_ADD_INS (cfg->cbb, ins);
12617 case CEE_MONO_LDNATIVEOBJ:
12619 * Similar to LDOBJ, but instead load the unmanaged
12620 * representation of the vtype to the stack.
12625 token = read32 (ip + 2);
12626 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12627 g_assert (klass->valuetype);
12628 mono_class_init (klass);
12631 MonoInst *src, *dest, *temp;
12634 temp = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
12635 temp->backend.is_pinvoke = 1;
12636 EMIT_NEW_TEMPLOADA (cfg, dest, temp->inst_c0);
12637 mini_emit_stobj (cfg, dest, src, klass, TRUE);
12639 EMIT_NEW_TEMPLOAD (cfg, dest, temp->inst_c0);
12640 dest->type = STACK_VTYPE;
12641 dest->klass = klass;
12647 case CEE_MONO_RETOBJ: {
12649 * Same as RET, but return the native representation of a vtype
12652 g_assert (cfg->ret);
12653 g_assert (mono_method_signature (method)->pinvoke);
12658 token = read32 (ip + 2);
12659 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12661 if (!cfg->vret_addr) {
12662 g_assert (cfg->ret_var_is_local);
12664 EMIT_NEW_VARLOADA (cfg, ins, cfg->ret, cfg->ret->inst_vtype);
12666 EMIT_NEW_RETLOADA (cfg, ins);
12668 mini_emit_stobj (cfg, ins, sp [0], klass, TRUE);
12670 if (sp != stack_start)
12673 MONO_INST_NEW (cfg, ins, OP_BR);
12674 ins->inst_target_bb = end_bblock;
12675 MONO_ADD_INS (cfg->cbb, ins);
12676 link_bblock (cfg, cfg->cbb, end_bblock);
12677 start_new_bblock = 1;
12681 case CEE_MONO_CISINST:
12682 case CEE_MONO_CCASTCLASS: {
12687 token = read32 (ip + 2);
12688 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12689 if (ip [1] == CEE_MONO_CISINST)
12690 ins = handle_cisinst (cfg, klass, sp [0]);
12692 ins = handle_ccastclass (cfg, klass, sp [0]);
12697 case CEE_MONO_SAVE_LMF:
12698 case CEE_MONO_RESTORE_LMF:
12701 case CEE_MONO_CLASSCONST:
12702 CHECK_STACK_OVF (1);
12704 token = read32 (ip + 2);
12705 EMIT_NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
12708 inline_costs += 10 * num_calls++;
12710 case CEE_MONO_NOT_TAKEN:
12711 cfg->cbb->out_of_line = TRUE;
12714 case CEE_MONO_TLS: {
12717 CHECK_STACK_OVF (1);
12719 key = (MonoTlsKey)read32 (ip + 2);
12720 g_assert (key < TLS_KEY_NUM);
12722 ins = mono_create_tls_get (cfg, key);
12724 if (cfg->compile_aot) {
12726 MONO_INST_NEW (cfg, ins, OP_TLS_GET);
12727 ins->dreg = alloc_preg (cfg);
12728 ins->type = STACK_PTR;
12730 g_assert_not_reached ();
12733 ins->type = STACK_PTR;
12734 MONO_ADD_INS (cfg->cbb, ins);
12739 case CEE_MONO_DYN_CALL: {
12740 MonoCallInst *call;
12742 /* It would be easier to call a trampoline, but that would put an
12743 * extra frame on the stack, confusing exception handling. So
12744 * implement it inline using an opcode for now.
12747 if (!cfg->dyn_call_var) {
12748 cfg->dyn_call_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12749 /* prevent it from being register allocated */
12750 cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
12753 /* Has to use a call inst since it local regalloc expects it */
12754 MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
12755 ins = (MonoInst*)call;
12757 ins->sreg1 = sp [0]->dreg;
12758 ins->sreg2 = sp [1]->dreg;
12759 MONO_ADD_INS (cfg->cbb, ins);
12761 cfg->param_area = MAX (cfg->param_area, cfg->backend->dyn_call_param_area);
12764 inline_costs += 10 * num_calls++;
12768 case CEE_MONO_MEMORY_BARRIER: {
12770 emit_memory_barrier (cfg, (int)read32 (ip + 2));
12774 case CEE_MONO_JIT_ATTACH: {
12775 MonoInst *args [16], *domain_ins;
12776 MonoInst *ad_ins, *jit_tls_ins;
12777 MonoBasicBlock *next_bb = NULL, *call_bb = NULL;
12779 g_assert (!mono_threads_is_coop_enabled ());
12781 cfg->orig_domain_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12783 EMIT_NEW_PCONST (cfg, ins, NULL);
12784 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12786 ad_ins = mono_get_domain_intrinsic (cfg);
12787 jit_tls_ins = mono_get_jit_tls_intrinsic (cfg);
12789 if (cfg->backend->have_tls_get && ad_ins && jit_tls_ins) {
12790 NEW_BBLOCK (cfg, next_bb);
12791 NEW_BBLOCK (cfg, call_bb);
12793 if (cfg->compile_aot) {
12794 /* AOT code is only used in the root domain */
12795 EMIT_NEW_PCONST (cfg, domain_ins, NULL);
12797 EMIT_NEW_PCONST (cfg, domain_ins, cfg->domain);
12799 MONO_ADD_INS (cfg->cbb, ad_ins);
12800 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, ad_ins->dreg, domain_ins->dreg);
12801 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, call_bb);
12803 MONO_ADD_INS (cfg->cbb, jit_tls_ins);
12804 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, jit_tls_ins->dreg, 0);
12805 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, call_bb);
12807 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, next_bb);
12808 MONO_START_BB (cfg, call_bb);
12811 /* AOT code is only used in the root domain */
12812 EMIT_NEW_PCONST (cfg, args [0], cfg->compile_aot ? NULL : cfg->domain);
12813 ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
12814 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12817 MONO_START_BB (cfg, next_bb);
12823 case CEE_MONO_JIT_DETACH: {
12824 MonoInst *args [16];
12826 /* Restore the original domain */
12827 dreg = alloc_ireg (cfg);
12828 EMIT_NEW_UNALU (cfg, args [0], OP_MOVE, dreg, cfg->orig_domain_var->dreg);
12829 mono_emit_jit_icall (cfg, mono_jit_set_domain, args);
12833 case CEE_MONO_CALLI_EXTRA_ARG: {
12835 MonoMethodSignature *fsig;
12839 * This is the same as CEE_CALLI, but passes an additional argument
12840 * to the called method in llvmonly mode.
12841 * This is only used by delegate invoke wrappers to call the
12842 * actual delegate method.
12844 g_assert (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE);
12847 token = read32 (ip + 2);
12855 fsig = mini_get_signature (method, token, generic_context, &cfg->error);
12858 if (cfg->llvm_only)
12859 cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, fsig);
12861 n = fsig->param_count + fsig->hasthis + 1;
12868 if (cfg->llvm_only) {
12870 * The lowest bit of 'arg' determines whenever the callee uses the gsharedvt
12871 * cconv. This is set by mono_init_delegate ().
12873 if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) {
12874 MonoInst *callee = addr;
12875 MonoInst *call, *localloc_ins;
12876 MonoBasicBlock *is_gsharedvt_bb, *end_bb;
12877 int low_bit_reg = alloc_preg (cfg);
12879 NEW_BBLOCK (cfg, is_gsharedvt_bb);
12880 NEW_BBLOCK (cfg, end_bb);
12882 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, low_bit_reg, arg->dreg, 1);
12883 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, low_bit_reg, 0);
12884 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_gsharedvt_bb);
12886 /* Normal case: callee uses a normal cconv, have to add an out wrapper */
12887 addr = emit_get_rgctx_sig (cfg, context_used,
12888 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
12890 * ADDR points to a gsharedvt-out wrapper, have to pass <callee, arg> as an extra arg.
12892 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
12893 ins->dreg = alloc_preg (cfg);
12894 ins->inst_imm = 2 * SIZEOF_VOID_P;
12895 MONO_ADD_INS (cfg->cbb, ins);
12896 localloc_ins = ins;
12897 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12898 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, 0, callee->dreg);
12899 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, SIZEOF_VOID_P, arg->dreg);
12901 call = emit_extra_arg_calli (cfg, fsig, sp, localloc_ins->dreg, addr);
12902 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12904 /* Gsharedvt case: callee uses a gsharedvt cconv, no conversion is needed */
12905 MONO_START_BB (cfg, is_gsharedvt_bb);
12906 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PXOR_IMM, arg->dreg, arg->dreg, 1);
12907 ins = emit_extra_arg_calli (cfg, fsig, sp, arg->dreg, callee);
12908 ins->dreg = call->dreg;
12910 MONO_START_BB (cfg, end_bb);
12912 /* Caller uses a normal calling conv */
12914 MonoInst *callee = addr;
12915 MonoInst *call, *localloc_ins;
12916 MonoBasicBlock *is_gsharedvt_bb, *end_bb;
12917 int low_bit_reg = alloc_preg (cfg);
12919 NEW_BBLOCK (cfg, is_gsharedvt_bb);
12920 NEW_BBLOCK (cfg, end_bb);
12922 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, low_bit_reg, arg->dreg, 1);
12923 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, low_bit_reg, 0);
12924 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_gsharedvt_bb);
12926 /* Normal case: callee uses a normal cconv, no conversion is needed */
12927 call = emit_extra_arg_calli (cfg, fsig, sp, arg->dreg, callee);
12928 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12929 /* Gsharedvt case: callee uses a gsharedvt cconv, have to add an in wrapper */
12930 MONO_START_BB (cfg, is_gsharedvt_bb);
12931 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PXOR_IMM, arg->dreg, arg->dreg, 1);
12932 NEW_AOTCONST (cfg, addr, MONO_PATCH_INFO_GSHAREDVT_IN_WRAPPER, fsig);
12933 MONO_ADD_INS (cfg->cbb, addr);
12935 * ADDR points to a gsharedvt-in wrapper, have to pass <callee, arg> as an extra arg.
12937 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
12938 ins->dreg = alloc_preg (cfg);
12939 ins->inst_imm = 2 * SIZEOF_VOID_P;
12940 MONO_ADD_INS (cfg->cbb, ins);
12941 localloc_ins = ins;
12942 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12943 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, 0, callee->dreg);
12944 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, SIZEOF_VOID_P, arg->dreg);
12946 ins = emit_extra_arg_calli (cfg, fsig, sp, localloc_ins->dreg, addr);
12947 ins->dreg = call->dreg;
12948 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12950 MONO_START_BB (cfg, end_bb);
12953 /* Same as CEE_CALLI */
12954 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
12956 * We pass the address to the gsharedvt trampoline in the rgctx reg
12958 MonoInst *callee = addr;
12960 addr = emit_get_rgctx_sig (cfg, context_used,
12961 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
12962 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
12964 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
12968 if (!MONO_TYPE_IS_VOID (fsig->ret))
12969 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
12971 CHECK_CFG_EXCEPTION;
12975 constrained_class = NULL;
12978 case CEE_MONO_LDDOMAIN:
12979 CHECK_STACK_OVF (1);
12980 EMIT_NEW_PCONST (cfg, ins, cfg->compile_aot ? NULL : cfg->domain);
12985 g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
12991 case CEE_PREFIX1: {
12994 case CEE_ARGLIST: {
12995 /* somewhat similar to LDTOKEN */
12996 MonoInst *addr, *vtvar;
12997 CHECK_STACK_OVF (1);
12998 vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL);
13000 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
13001 EMIT_NEW_UNALU (cfg, ins, OP_ARGLIST, -1, addr->dreg);
13003 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
13004 ins->type = STACK_VTYPE;
13005 ins->klass = mono_defaults.argumenthandle_class;
13015 MonoInst *cmp, *arg1, *arg2;
13023 * The following transforms:
13024 * CEE_CEQ into OP_CEQ
13025 * CEE_CGT into OP_CGT
13026 * CEE_CGT_UN into OP_CGT_UN
13027 * CEE_CLT into OP_CLT
13028 * CEE_CLT_UN into OP_CLT_UN
13030 MONO_INST_NEW (cfg, cmp, (OP_CEQ - CEE_CEQ) + ip [1]);
13032 MONO_INST_NEW (cfg, ins, cmp->opcode);
13033 cmp->sreg1 = arg1->dreg;
13034 cmp->sreg2 = arg2->dreg;
13035 type_from_op (cfg, cmp, arg1, arg2);
13037 add_widen_op (cfg, cmp, &arg1, &arg2);
13038 if ((arg1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((arg1->type == STACK_PTR) || (arg1->type == STACK_OBJ) || (arg1->type == STACK_MP))))
13039 cmp->opcode = OP_LCOMPARE;
13040 else if (arg1->type == STACK_R4)
13041 cmp->opcode = OP_RCOMPARE;
13042 else if (arg1->type == STACK_R8)
13043 cmp->opcode = OP_FCOMPARE;
13045 cmp->opcode = OP_ICOMPARE;
13046 MONO_ADD_INS (cfg->cbb, cmp);
13047 ins->type = STACK_I4;
13048 ins->dreg = alloc_dreg (cfg, (MonoStackType)ins->type);
13049 type_from_op (cfg, ins, arg1, arg2);
13051 if (cmp->opcode == OP_FCOMPARE || cmp->opcode == OP_RCOMPARE) {
13053 * The backends expect the fceq opcodes to do the
13056 ins->sreg1 = cmp->sreg1;
13057 ins->sreg2 = cmp->sreg2;
13060 MONO_ADD_INS (cfg->cbb, ins);
13066 MonoInst *argconst;
13067 MonoMethod *cil_method;
13069 CHECK_STACK_OVF (1);
13071 n = read32 (ip + 2);
13072 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
13075 mono_class_init (cmethod->klass);
13077 mono_save_token_info (cfg, image, n, cmethod);
13079 context_used = mini_method_check_context_used (cfg, cmethod);
13081 cil_method = cmethod;
13082 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
13083 METHOD_ACCESS_FAILURE (method, cil_method);
13085 if (mono_security_core_clr_enabled ())
13086 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
13089 * Optimize the common case of ldftn+delegate creation
13091 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
13092 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
13093 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
13094 MonoInst *target_ins, *handle_ins;
13095 MonoMethod *invoke;
13096 int invoke_context_used;
13098 invoke = mono_get_delegate_invoke (ctor_method->klass);
13099 if (!invoke || !mono_method_signature (invoke))
13102 invoke_context_used = mini_method_check_context_used (cfg, invoke);
13104 target_ins = sp [-1];
13106 if (mono_security_core_clr_enabled ())
13107 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method);
13109 if (!(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) {
13110 /*LAME IMPL: We must not add a null check for virtual invoke delegates.*/
13111 if (mono_method_signature (invoke)->param_count == mono_method_signature (cmethod)->param_count) {
13112 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target_ins->dreg, 0);
13113 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "ArgumentException");
13117 /* FIXME: SGEN support */
13118 if (invoke_context_used == 0 || cfg->llvm_only) {
13120 if (cfg->verbose_level > 3)
13121 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));
13122 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, FALSE))) {
13125 CHECK_CFG_EXCEPTION;
13135 argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
13136 ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
13140 inline_costs += 10 * num_calls++;
13143 case CEE_LDVIRTFTN: {
13144 MonoInst *args [2];
13148 n = read32 (ip + 2);
13149 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
13152 mono_class_init (cmethod->klass);
13154 context_used = mini_method_check_context_used (cfg, cmethod);
13156 if (mono_security_core_clr_enabled ())
13157 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
13160 * Optimize the common case of ldvirtftn+delegate creation
13162 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)) {
13163 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
13164 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
13165 MonoInst *target_ins, *handle_ins;
13166 MonoMethod *invoke;
13167 int invoke_context_used;
13168 gboolean is_virtual = cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL;
13170 invoke = mono_get_delegate_invoke (ctor_method->klass);
13171 if (!invoke || !mono_method_signature (invoke))
13174 invoke_context_used = mini_method_check_context_used (cfg, invoke);
13176 target_ins = sp [-1];
13178 if (mono_security_core_clr_enabled ())
13179 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method);
13181 /* FIXME: SGEN support */
13182 if (invoke_context_used == 0 || cfg->llvm_only) {
13184 if (cfg->verbose_level > 3)
13185 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));
13186 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, is_virtual))) {
13189 CHECK_CFG_EXCEPTION;
13202 args [1] = emit_get_rgctx_method (cfg, context_used,
13203 cmethod, MONO_RGCTX_INFO_METHOD);
13206 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
13208 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
13211 inline_costs += 10 * num_calls++;
13215 CHECK_STACK_OVF (1);
13217 n = read16 (ip + 2);
13219 EMIT_NEW_ARGLOAD (cfg, ins, n);
13224 CHECK_STACK_OVF (1);
13226 n = read16 (ip + 2);
13228 NEW_ARGLOADA (cfg, ins, n);
13229 MONO_ADD_INS (cfg->cbb, ins);
13237 n = read16 (ip + 2);
13239 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
13241 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
13245 CHECK_STACK_OVF (1);
13247 n = read16 (ip + 2);
13249 EMIT_NEW_LOCLOAD (cfg, ins, n);
13254 unsigned char *tmp_ip;
13255 CHECK_STACK_OVF (1);
13257 n = read16 (ip + 2);
13260 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 2))) {
13266 EMIT_NEW_LOCLOADA (cfg, ins, n);
13275 n = read16 (ip + 2);
13277 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
13279 emit_stloc_ir (cfg, sp, header, n);
13286 if (sp != stack_start)
13288 if (cfg->method != method)
13290 * Inlining this into a loop in a parent could lead to
13291 * stack overflows which is different behavior than the
13292 * non-inlined case, thus disable inlining in this case.
13294 INLINE_FAILURE("localloc");
13296 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
13297 ins->dreg = alloc_preg (cfg);
13298 ins->sreg1 = sp [0]->dreg;
13299 ins->type = STACK_PTR;
13300 MONO_ADD_INS (cfg->cbb, ins);
13302 cfg->flags |= MONO_CFG_HAS_ALLOCA;
13304 ins->flags |= MONO_INST_INIT;
13309 case CEE_ENDFILTER: {
13310 MonoExceptionClause *clause, *nearest;
13315 if ((sp != stack_start) || (sp [0]->type != STACK_I4))
13317 MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
13318 ins->sreg1 = (*sp)->dreg;
13319 MONO_ADD_INS (cfg->cbb, ins);
13320 start_new_bblock = 1;
13324 for (cc = 0; cc < header->num_clauses; ++cc) {
13325 clause = &header->clauses [cc];
13326 if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
13327 ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) &&
13328 (!nearest || (clause->data.filter_offset < nearest->data.filter_offset)))
13331 g_assert (nearest);
13332 if ((ip - header->code) != nearest->handler_offset)
13337 case CEE_UNALIGNED_:
13338 ins_flag |= MONO_INST_UNALIGNED;
13339 /* FIXME: record alignment? we can assume 1 for now */
13343 case CEE_VOLATILE_:
13344 ins_flag |= MONO_INST_VOLATILE;
13348 ins_flag |= MONO_INST_TAILCALL;
13349 cfg->flags |= MONO_CFG_HAS_TAIL;
13350 /* Can't inline tail calls at this time */
13351 inline_costs += 100000;
13358 token = read32 (ip + 2);
13359 klass = mini_get_class (method, token, generic_context);
13360 CHECK_TYPELOAD (klass);
13361 if (generic_class_is_reference_type (cfg, klass))
13362 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sp [0]->dreg, 0, 0);
13364 mini_emit_initobj (cfg, *sp, NULL, klass);
13368 case CEE_CONSTRAINED_:
13370 token = read32 (ip + 2);
13371 constrained_class = mini_get_class (method, token, generic_context);
13372 CHECK_TYPELOAD (constrained_class);
13376 case CEE_INITBLK: {
13377 MonoInst *iargs [3];
13381 /* Skip optimized paths for volatile operations. */
13382 if ((ip [1] == CEE_CPBLK) && !(ins_flag & MONO_INST_VOLATILE) && (cfg->opt & MONO_OPT_INTRINS) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5)) {
13383 mini_emit_memcpy (cfg, sp [0]->dreg, 0, sp [1]->dreg, 0, sp [2]->inst_c0, 0);
13384 } else if ((ip [1] == CEE_INITBLK) && !(ins_flag & MONO_INST_VOLATILE) && (cfg->opt & MONO_OPT_INTRINS) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5) && (sp [1]->opcode == OP_ICONST) && (sp [1]->inst_c0 == 0)) {
13385 /* emit_memset only works when val == 0 */
13386 mini_emit_memset (cfg, sp [0]->dreg, 0, sp [2]->inst_c0, sp [1]->inst_c0, 0);
13389 iargs [0] = sp [0];
13390 iargs [1] = sp [1];
13391 iargs [2] = sp [2];
13392 if (ip [1] == CEE_CPBLK) {
13394 * FIXME: It's unclear whether we should be emitting both the acquire
13395 * and release barriers for cpblk. It is technically both a load and
13396 * store operation, so it seems like that's the sensible thing to do.
13398 * FIXME: We emit full barriers on both sides of the operation for
13399 * simplicity. We should have a separate atomic memcpy method instead.
13401 MonoMethod *memcpy_method = get_memcpy_method ();
13403 if (ins_flag & MONO_INST_VOLATILE)
13404 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
13406 call = mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
13407 call->flags |= ins_flag;
13409 if (ins_flag & MONO_INST_VOLATILE)
13410 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
13412 MonoMethod *memset_method = get_memset_method ();
13413 if (ins_flag & MONO_INST_VOLATILE) {
13414 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
13415 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
13417 call = mono_emit_method_call (cfg, memset_method, iargs, NULL);
13418 call->flags |= ins_flag;
13429 ins_flag |= MONO_INST_NOTYPECHECK;
13431 ins_flag |= MONO_INST_NORANGECHECK;
13432 /* we ignore the no-nullcheck for now since we
13433 * really do it explicitly only when doing callvirt->call
13437 case CEE_RETHROW: {
13439 int handler_offset = -1;
13441 for (i = 0; i < header->num_clauses; ++i) {
13442 MonoExceptionClause *clause = &header->clauses [i];
13443 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && !(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
13444 handler_offset = clause->handler_offset;
13449 cfg->cbb->flags |= BB_EXCEPTION_UNSAFE;
13451 if (handler_offset == -1)
13454 EMIT_NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
13455 MONO_INST_NEW (cfg, ins, OP_RETHROW);
13456 ins->sreg1 = load->dreg;
13457 MONO_ADD_INS (cfg->cbb, ins);
13459 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
13460 MONO_ADD_INS (cfg->cbb, ins);
13463 link_bblock (cfg, cfg->cbb, end_bblock);
13464 start_new_bblock = 1;
13472 CHECK_STACK_OVF (1);
13474 token = read32 (ip + 2);
13475 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (method->klass->image) && !generic_context) {
13476 MonoType *type = mono_type_create_from_typespec_checked (image, token, &cfg->error);
13479 val = mono_type_size (type, &ialign);
13481 MonoClass *klass = mini_get_class (method, token, generic_context);
13482 CHECK_TYPELOAD (klass);
13484 val = mono_type_size (&klass->byval_arg, &ialign);
13486 if (mini_is_gsharedvt_klass (klass))
13487 GSHAREDVT_FAILURE (*ip);
13489 EMIT_NEW_ICONST (cfg, ins, val);
13494 case CEE_REFANYTYPE: {
13495 MonoInst *src_var, *src;
13497 GSHAREDVT_FAILURE (*ip);
13503 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
13505 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
13506 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
13507 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &mono_defaults.typehandle_class->byval_arg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type));
13512 case CEE_READONLY_:
13525 g_warning ("opcode 0xfe 0x%02x not handled", ip [1]);
13535 g_warning ("opcode 0x%02x not handled", *ip);
13539 if (start_new_bblock != 1)
13542 cfg->cbb->cil_length = ip - cfg->cbb->cil_code;
13543 if (cfg->cbb->next_bb) {
13544 /* This could already be set because of inlining, #693905 */
13545 MonoBasicBlock *bb = cfg->cbb;
13547 while (bb->next_bb)
13549 bb->next_bb = end_bblock;
13551 cfg->cbb->next_bb = end_bblock;
13554 if (cfg->method == method && cfg->domainvar) {
13556 MonoInst *get_domain;
13558 cfg->cbb = init_localsbb;
13560 if ((get_domain = mono_get_domain_intrinsic (cfg))) {
13561 MONO_ADD_INS (cfg->cbb, get_domain);
13563 get_domain = mono_emit_jit_icall (cfg, mono_domain_get, NULL);
13565 NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain);
13566 MONO_ADD_INS (cfg->cbb, store);
13569 #if defined(TARGET_POWERPC) || defined(TARGET_X86)
13570 if (cfg->compile_aot)
13571 /* FIXME: The plt slots require a GOT var even if the method doesn't use it */
13572 mono_get_got_var (cfg);
13575 if (cfg->method == method && cfg->got_var)
13576 mono_emit_load_got_addr (cfg);
13578 if (init_localsbb) {
13579 cfg->cbb = init_localsbb;
13581 for (i = 0; i < header->num_locals; ++i) {
13582 emit_init_local (cfg, i, header->locals [i], init_locals);
13586 if (cfg->init_ref_vars && cfg->method == method) {
13587 /* Emit initialization for ref vars */
13588 // FIXME: Avoid duplication initialization for IL locals.
13589 for (i = 0; i < cfg->num_varinfo; ++i) {
13590 MonoInst *ins = cfg->varinfo [i];
13592 if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ)
13593 MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL);
13597 if (cfg->lmf_var && cfg->method == method && !cfg->llvm_only) {
13598 cfg->cbb = init_localsbb;
13599 emit_push_lmf (cfg);
13602 cfg->cbb = init_localsbb;
13603 emit_instrumentation_call (cfg, mono_profiler_method_enter);
13606 MonoBasicBlock *bb;
13609 * Make seq points at backward branch targets interruptable.
13611 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
13612 if (bb->code && bb->in_count > 1 && bb->code->opcode == OP_SEQ_POINT)
13613 bb->code->flags |= MONO_INST_SINGLE_STEP_LOC;
13616 /* Add a sequence point for method entry/exit events */
13617 if (seq_points && cfg->gen_sdb_seq_points) {
13618 NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
13619 MONO_ADD_INS (init_localsbb, ins);
13620 NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
13621 MONO_ADD_INS (cfg->bb_exit, ins);
13625 * Add seq points for IL offsets which have line number info, but wasn't generated a seq point during JITting because
13626 * the code they refer to was dead (#11880).
13628 if (sym_seq_points) {
13629 for (i = 0; i < header->code_size; ++i) {
13630 if (mono_bitset_test_fast (seq_point_locs, i) && !mono_bitset_test_fast (seq_point_set_locs, i)) {
13633 NEW_SEQ_POINT (cfg, ins, i, FALSE);
13634 mono_add_seq_point (cfg, NULL, ins, SEQ_POINT_NATIVE_OFFSET_DEAD_CODE);
13641 if (cfg->method == method) {
13642 MonoBasicBlock *bb;
13643 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13644 bb->region = mono_find_block_region (cfg, bb->real_offset);
13646 mono_create_spvar_for_region (cfg, bb->region);
13647 if (cfg->verbose_level > 2)
13648 printf ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
13651 MonoBasicBlock *bb;
13652 /* get_most_deep_clause () in mini-llvm.c depends on this for inlined bblocks */
13653 for (bb = start_bblock; bb != end_bblock; bb = bb->next_bb) {
13654 bb->real_offset = inline_offset;
13658 if (inline_costs < 0) {
13661 /* Method is too large */
13662 mname = mono_method_full_name (method, TRUE);
13663 mono_cfg_set_exception_invalid_program (cfg, g_strdup_printf ("Method %s is too complex.", mname));
13667 if ((cfg->verbose_level > 2) && (cfg->method == method))
13668 mono_print_code (cfg, "AFTER METHOD-TO-IR");
13673 g_assert (!mono_error_ok (&cfg->error));
13677 g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
13681 set_exception_type_from_invalid_il (cfg, method, ip);
13685 g_slist_free (class_inits);
13686 mono_basic_block_free (original_bb);
13687 cfg->dont_inline = g_list_remove (cfg->dont_inline, method);
13688 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
13689 if (cfg->exception_type)
13692 return inline_costs;
13696 store_membase_reg_to_store_membase_imm (int opcode)
13699 case OP_STORE_MEMBASE_REG:
13700 return OP_STORE_MEMBASE_IMM;
13701 case OP_STOREI1_MEMBASE_REG:
13702 return OP_STOREI1_MEMBASE_IMM;
13703 case OP_STOREI2_MEMBASE_REG:
13704 return OP_STOREI2_MEMBASE_IMM;
13705 case OP_STOREI4_MEMBASE_REG:
13706 return OP_STOREI4_MEMBASE_IMM;
13707 case OP_STOREI8_MEMBASE_REG:
13708 return OP_STOREI8_MEMBASE_IMM;
13710 g_assert_not_reached ();
13717 mono_op_to_op_imm (int opcode)
13721 return OP_IADD_IMM;
13723 return OP_ISUB_IMM;
13725 return OP_IDIV_IMM;
13727 return OP_IDIV_UN_IMM;
13729 return OP_IREM_IMM;
13731 return OP_IREM_UN_IMM;
13733 return OP_IMUL_IMM;
13735 return OP_IAND_IMM;
13739 return OP_IXOR_IMM;
13741 return OP_ISHL_IMM;
13743 return OP_ISHR_IMM;
13745 return OP_ISHR_UN_IMM;
13748 return OP_LADD_IMM;
13750 return OP_LSUB_IMM;
13752 return OP_LAND_IMM;
13756 return OP_LXOR_IMM;
13758 return OP_LSHL_IMM;
13760 return OP_LSHR_IMM;
13762 return OP_LSHR_UN_IMM;
13763 #if SIZEOF_REGISTER == 8
13765 return OP_LREM_IMM;
13769 return OP_COMPARE_IMM;
13771 return OP_ICOMPARE_IMM;
13773 return OP_LCOMPARE_IMM;
13775 case OP_STORE_MEMBASE_REG:
13776 return OP_STORE_MEMBASE_IMM;
13777 case OP_STOREI1_MEMBASE_REG:
13778 return OP_STOREI1_MEMBASE_IMM;
13779 case OP_STOREI2_MEMBASE_REG:
13780 return OP_STOREI2_MEMBASE_IMM;
13781 case OP_STOREI4_MEMBASE_REG:
13782 return OP_STOREI4_MEMBASE_IMM;
13784 #if defined(TARGET_X86) || defined (TARGET_AMD64)
13786 return OP_X86_PUSH_IMM;
13787 case OP_X86_COMPARE_MEMBASE_REG:
13788 return OP_X86_COMPARE_MEMBASE_IMM;
13790 #if defined(TARGET_AMD64)
13791 case OP_AMD64_ICOMPARE_MEMBASE_REG:
13792 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13794 case OP_VOIDCALL_REG:
13795 return OP_VOIDCALL;
13803 return OP_LOCALLOC_IMM;
13810 ldind_to_load_membase (int opcode)
13814 return OP_LOADI1_MEMBASE;
13816 return OP_LOADU1_MEMBASE;
13818 return OP_LOADI2_MEMBASE;
13820 return OP_LOADU2_MEMBASE;
13822 return OP_LOADI4_MEMBASE;
13824 return OP_LOADU4_MEMBASE;
13826 return OP_LOAD_MEMBASE;
13827 case CEE_LDIND_REF:
13828 return OP_LOAD_MEMBASE;
13830 return OP_LOADI8_MEMBASE;
13832 return OP_LOADR4_MEMBASE;
13834 return OP_LOADR8_MEMBASE;
13836 g_assert_not_reached ();
13843 stind_to_store_membase (int opcode)
13847 return OP_STOREI1_MEMBASE_REG;
13849 return OP_STOREI2_MEMBASE_REG;
13851 return OP_STOREI4_MEMBASE_REG;
13853 case CEE_STIND_REF:
13854 return OP_STORE_MEMBASE_REG;
13856 return OP_STOREI8_MEMBASE_REG;
13858 return OP_STORER4_MEMBASE_REG;
13860 return OP_STORER8_MEMBASE_REG;
13862 g_assert_not_reached ();
13869 mono_load_membase_to_load_mem (int opcode)
13871 // FIXME: Add a MONO_ARCH_HAVE_LOAD_MEM macro
13872 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13874 case OP_LOAD_MEMBASE:
13875 return OP_LOAD_MEM;
13876 case OP_LOADU1_MEMBASE:
13877 return OP_LOADU1_MEM;
13878 case OP_LOADU2_MEMBASE:
13879 return OP_LOADU2_MEM;
13880 case OP_LOADI4_MEMBASE:
13881 return OP_LOADI4_MEM;
13882 case OP_LOADU4_MEMBASE:
13883 return OP_LOADU4_MEM;
13884 #if SIZEOF_REGISTER == 8
13885 case OP_LOADI8_MEMBASE:
13886 return OP_LOADI8_MEM;
13895 op_to_op_dest_membase (int store_opcode, int opcode)
13897 #if defined(TARGET_X86)
13898 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG)))
13903 return OP_X86_ADD_MEMBASE_REG;
13905 return OP_X86_SUB_MEMBASE_REG;
13907 return OP_X86_AND_MEMBASE_REG;
13909 return OP_X86_OR_MEMBASE_REG;
13911 return OP_X86_XOR_MEMBASE_REG;
13914 return OP_X86_ADD_MEMBASE_IMM;
13917 return OP_X86_SUB_MEMBASE_IMM;
13920 return OP_X86_AND_MEMBASE_IMM;
13923 return OP_X86_OR_MEMBASE_IMM;
13926 return OP_X86_XOR_MEMBASE_IMM;
13932 #if defined(TARGET_AMD64)
13933 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG) || (store_opcode == OP_STOREI8_MEMBASE_REG)))
13938 return OP_X86_ADD_MEMBASE_REG;
13940 return OP_X86_SUB_MEMBASE_REG;
13942 return OP_X86_AND_MEMBASE_REG;
13944 return OP_X86_OR_MEMBASE_REG;
13946 return OP_X86_XOR_MEMBASE_REG;
13948 return OP_X86_ADD_MEMBASE_IMM;
13950 return OP_X86_SUB_MEMBASE_IMM;
13952 return OP_X86_AND_MEMBASE_IMM;
13954 return OP_X86_OR_MEMBASE_IMM;
13956 return OP_X86_XOR_MEMBASE_IMM;
13958 return OP_AMD64_ADD_MEMBASE_REG;
13960 return OP_AMD64_SUB_MEMBASE_REG;
13962 return OP_AMD64_AND_MEMBASE_REG;
13964 return OP_AMD64_OR_MEMBASE_REG;
13966 return OP_AMD64_XOR_MEMBASE_REG;
13969 return OP_AMD64_ADD_MEMBASE_IMM;
13972 return OP_AMD64_SUB_MEMBASE_IMM;
13975 return OP_AMD64_AND_MEMBASE_IMM;
13978 return OP_AMD64_OR_MEMBASE_IMM;
13981 return OP_AMD64_XOR_MEMBASE_IMM;
13991 op_to_op_store_membase (int store_opcode, int opcode)
13993 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13996 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13997 return OP_X86_SETEQ_MEMBASE;
13999 if (store_opcode == OP_STOREI1_MEMBASE_REG)
14000 return OP_X86_SETNE_MEMBASE;
14008 op_to_op_src1_membase (MonoCompile *cfg, int load_opcode, int opcode)
14011 /* FIXME: This has sign extension issues */
14013 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
14014 return OP_X86_COMPARE_MEMBASE8_IMM;
14017 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
14022 return OP_X86_PUSH_MEMBASE;
14023 case OP_COMPARE_IMM:
14024 case OP_ICOMPARE_IMM:
14025 return OP_X86_COMPARE_MEMBASE_IMM;
14028 return OP_X86_COMPARE_MEMBASE_REG;
14032 #ifdef TARGET_AMD64
14033 /* FIXME: This has sign extension issues */
14035 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
14036 return OP_X86_COMPARE_MEMBASE8_IMM;
14041 if ((load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32) || (load_opcode == OP_LOADI8_MEMBASE))
14042 return OP_X86_PUSH_MEMBASE;
14044 /* FIXME: This only works for 32 bit immediates
14045 case OP_COMPARE_IMM:
14046 case OP_LCOMPARE_IMM:
14047 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
14048 return OP_AMD64_COMPARE_MEMBASE_IMM;
14050 case OP_ICOMPARE_IMM:
14051 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
14052 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
14056 if (cfg->backend->ilp32 && load_opcode == OP_LOAD_MEMBASE)
14057 return OP_AMD64_ICOMPARE_MEMBASE_REG;
14058 if ((load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32) || (load_opcode == OP_LOADI8_MEMBASE))
14059 return OP_AMD64_COMPARE_MEMBASE_REG;
14062 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
14063 return OP_AMD64_ICOMPARE_MEMBASE_REG;
14072 op_to_op_src2_membase (MonoCompile *cfg, int load_opcode, int opcode)
14075 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
14081 return OP_X86_COMPARE_REG_MEMBASE;
14083 return OP_X86_ADD_REG_MEMBASE;
14085 return OP_X86_SUB_REG_MEMBASE;
14087 return OP_X86_AND_REG_MEMBASE;
14089 return OP_X86_OR_REG_MEMBASE;
14091 return OP_X86_XOR_REG_MEMBASE;
14095 #ifdef TARGET_AMD64
14096 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE && cfg->backend->ilp32)) {
14099 return OP_AMD64_ICOMPARE_REG_MEMBASE;
14101 return OP_X86_ADD_REG_MEMBASE;
14103 return OP_X86_SUB_REG_MEMBASE;
14105 return OP_X86_AND_REG_MEMBASE;
14107 return OP_X86_OR_REG_MEMBASE;
14109 return OP_X86_XOR_REG_MEMBASE;
14111 } else if ((load_opcode == OP_LOADI8_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32)) {
14115 return OP_AMD64_COMPARE_REG_MEMBASE;
14117 return OP_AMD64_ADD_REG_MEMBASE;
14119 return OP_AMD64_SUB_REG_MEMBASE;
14121 return OP_AMD64_AND_REG_MEMBASE;
14123 return OP_AMD64_OR_REG_MEMBASE;
14125 return OP_AMD64_XOR_REG_MEMBASE;
14134 mono_op_to_op_imm_noemul (int opcode)
14137 #if SIZEOF_REGISTER == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS)
14143 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
14150 #if defined(MONO_ARCH_EMULATE_MUL_DIV)
14155 return mono_op_to_op_imm (opcode);
14160 * mono_handle_global_vregs:
14162 * Make vregs used in more than one bblock 'global', i.e. allocate a variable
14166 mono_handle_global_vregs (MonoCompile *cfg)
14168 gint32 *vreg_to_bb;
14169 MonoBasicBlock *bb;
14172 vreg_to_bb = (gint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (gint32*) * cfg->next_vreg + 1);
14174 #ifdef MONO_ARCH_SIMD_INTRINSICS
14175 if (cfg->uses_simd_intrinsics)
14176 mono_simd_simplify_indirection (cfg);
14179 /* Find local vregs used in more than one bb */
14180 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
14181 MonoInst *ins = bb->code;
14182 int block_num = bb->block_num;
14184 if (cfg->verbose_level > 2)
14185 printf ("\nHANDLE-GLOBAL-VREGS BLOCK %d:\n", bb->block_num);
14188 for (; ins; ins = ins->next) {
14189 const char *spec = INS_INFO (ins->opcode);
14190 int regtype = 0, regindex;
14193 if (G_UNLIKELY (cfg->verbose_level > 2))
14194 mono_print_ins (ins);
14196 g_assert (ins->opcode >= MONO_CEE_LAST);
14198 for (regindex = 0; regindex < 4; regindex ++) {
14201 if (regindex == 0) {
14202 regtype = spec [MONO_INST_DEST];
14203 if (regtype == ' ')
14206 } else if (regindex == 1) {
14207 regtype = spec [MONO_INST_SRC1];
14208 if (regtype == ' ')
14211 } else if (regindex == 2) {
14212 regtype = spec [MONO_INST_SRC2];
14213 if (regtype == ' ')
14216 } else if (regindex == 3) {
14217 regtype = spec [MONO_INST_SRC3];
14218 if (regtype == ' ')
14223 #if SIZEOF_REGISTER == 4
14224 /* In the LLVM case, the long opcodes are not decomposed */
14225 if (regtype == 'l' && !COMPILE_LLVM (cfg)) {
14227 * Since some instructions reference the original long vreg,
14228 * and some reference the two component vregs, it is quite hard
14229 * to determine when it needs to be global. So be conservative.
14231 if (!get_vreg_to_inst (cfg, vreg)) {
14232 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
14234 if (cfg->verbose_level > 2)
14235 printf ("LONG VREG R%d made global.\n", vreg);
14239 * Make the component vregs volatile since the optimizations can
14240 * get confused otherwise.
14242 get_vreg_to_inst (cfg, MONO_LVREG_LS (vreg))->flags |= MONO_INST_VOLATILE;
14243 get_vreg_to_inst (cfg, MONO_LVREG_MS (vreg))->flags |= MONO_INST_VOLATILE;
14247 g_assert (vreg != -1);
14249 prev_bb = vreg_to_bb [vreg];
14250 if (prev_bb == 0) {
14251 /* 0 is a valid block num */
14252 vreg_to_bb [vreg] = block_num + 1;
14253 } else if ((prev_bb != block_num + 1) && (prev_bb != -1)) {
14254 if (((regtype == 'i' && (vreg < MONO_MAX_IREGS))) || (regtype == 'f' && (vreg < MONO_MAX_FREGS)))
14257 if (!get_vreg_to_inst (cfg, vreg)) {
14258 if (G_UNLIKELY (cfg->verbose_level > 2))
14259 printf ("VREG R%d used in BB%d and BB%d made global.\n", vreg, vreg_to_bb [vreg], block_num);
14263 if (vreg_is_ref (cfg, vreg))
14264 mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, vreg);
14266 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg);
14269 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
14272 mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg);
14275 mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, vreg);
14278 g_assert_not_reached ();
14282 /* Flag as having been used in more than one bb */
14283 vreg_to_bb [vreg] = -1;
14289 /* If a variable is used in only one bblock, convert it into a local vreg */
14290 for (i = 0; i < cfg->num_varinfo; i++) {
14291 MonoInst *var = cfg->varinfo [i];
14292 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
14294 switch (var->type) {
14300 #if SIZEOF_REGISTER == 8
14303 #if !defined(TARGET_X86)
14304 /* Enabling this screws up the fp stack on x86 */
14307 if (mono_arch_is_soft_float ())
14311 if (var->type == STACK_VTYPE && cfg->gsharedvt && mini_is_gsharedvt_variable_type (var->inst_vtype))
14315 /* Arguments are implicitly global */
14316 /* Putting R4 vars into registers doesn't work currently */
14317 /* The gsharedvt vars are implicitly referenced by ldaddr opcodes, but those opcodes are only generated later */
14318 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) {
14320 * Make that the variable's liveness interval doesn't contain a call, since
14321 * that would cause the lvreg to be spilled, making the whole optimization
14324 /* This is too slow for JIT compilation */
14326 if (cfg->compile_aot && vreg_to_bb [var->dreg]) {
14328 int def_index, call_index, ins_index;
14329 gboolean spilled = FALSE;
14334 for (ins = vreg_to_bb [var->dreg]->code; ins; ins = ins->next) {
14335 const char *spec = INS_INFO (ins->opcode);
14337 if ((spec [MONO_INST_DEST] != ' ') && (ins->dreg == var->dreg))
14338 def_index = ins_index;
14340 if (((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg)) ||
14341 ((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg))) {
14342 if (call_index > def_index) {
14348 if (MONO_IS_CALL (ins))
14349 call_index = ins_index;
14359 if (G_UNLIKELY (cfg->verbose_level > 2))
14360 printf ("CONVERTED R%d(%d) TO VREG.\n", var->dreg, vmv->idx);
14361 var->flags |= MONO_INST_IS_DEAD;
14362 cfg->vreg_to_inst [var->dreg] = NULL;
14369 * Compress the varinfo and vars tables so the liveness computation is faster and
14370 * takes up less space.
14373 for (i = 0; i < cfg->num_varinfo; ++i) {
14374 MonoInst *var = cfg->varinfo [i];
14375 if (pos < i && cfg->locals_start == i)
14376 cfg->locals_start = pos;
14377 if (!(var->flags & MONO_INST_IS_DEAD)) {
14379 cfg->varinfo [pos] = cfg->varinfo [i];
14380 cfg->varinfo [pos]->inst_c0 = pos;
14381 memcpy (&cfg->vars [pos], &cfg->vars [i], sizeof (MonoMethodVar));
14382 cfg->vars [pos].idx = pos;
14383 #if SIZEOF_REGISTER == 4
14384 if (cfg->varinfo [pos]->type == STACK_I8) {
14385 /* Modify the two component vars too */
14388 var1 = get_vreg_to_inst (cfg, MONO_LVREG_LS (cfg->varinfo [pos]->dreg));
14389 var1->inst_c0 = pos;
14390 var1 = get_vreg_to_inst (cfg, MONO_LVREG_MS (cfg->varinfo [pos]->dreg));
14391 var1->inst_c0 = pos;
14398 cfg->num_varinfo = pos;
14399 if (cfg->locals_start > cfg->num_varinfo)
14400 cfg->locals_start = cfg->num_varinfo;
14404 * mono_allocate_gsharedvt_vars:
14406 * Allocate variables with gsharedvt types to entries in the MonoGSharedVtMethodRuntimeInfo.entries array.
14407 * Initialize cfg->gsharedvt_vreg_to_idx with the mapping between vregs and indexes.
14410 mono_allocate_gsharedvt_vars (MonoCompile *cfg)
14414 cfg->gsharedvt_vreg_to_idx = (int *)mono_mempool_alloc0 (cfg->mempool, sizeof (int) * cfg->next_vreg);
14416 for (i = 0; i < cfg->num_varinfo; ++i) {
14417 MonoInst *ins = cfg->varinfo [i];
14420 if (mini_is_gsharedvt_variable_type (ins->inst_vtype)) {
14421 if (i >= cfg->locals_start) {
14423 idx = get_gsharedvt_info_slot (cfg, ins->inst_vtype, MONO_RGCTX_INFO_LOCAL_OFFSET);
14424 cfg->gsharedvt_vreg_to_idx [ins->dreg] = idx + 1;
14425 ins->opcode = OP_GSHAREDVT_LOCAL;
14426 ins->inst_imm = idx;
14429 cfg->gsharedvt_vreg_to_idx [ins->dreg] = -1;
14430 ins->opcode = OP_GSHAREDVT_ARG_REGOFFSET;
14437 * mono_spill_global_vars:
14439 * Generate spill code for variables which are not allocated to registers,
14440 * and replace vregs with their allocated hregs. *need_local_opts is set to TRUE if
14441 * code is generated which could be optimized by the local optimization passes.
14444 mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
14446 MonoBasicBlock *bb;
14448 int orig_next_vreg;
14449 guint32 *vreg_to_lvreg;
14451 guint32 i, lvregs_len;
14452 gboolean dest_has_lvreg = FALSE;
14453 MonoStackType stacktypes [128];
14454 MonoInst **live_range_start, **live_range_end;
14455 MonoBasicBlock **live_range_start_bb, **live_range_end_bb;
14457 *need_local_opts = FALSE;
14459 memset (spec2, 0, sizeof (spec2));
14461 /* FIXME: Move this function to mini.c */
14462 stacktypes ['i'] = STACK_PTR;
14463 stacktypes ['l'] = STACK_I8;
14464 stacktypes ['f'] = STACK_R8;
14465 #ifdef MONO_ARCH_SIMD_INTRINSICS
14466 stacktypes ['x'] = STACK_VTYPE;
14469 #if SIZEOF_REGISTER == 4
14470 /* Create MonoInsts for longs */
14471 for (i = 0; i < cfg->num_varinfo; i++) {
14472 MonoInst *ins = cfg->varinfo [i];
14474 if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) {
14475 switch (ins->type) {
14480 if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg))
14483 g_assert (ins->opcode == OP_REGOFFSET);
14485 tree = get_vreg_to_inst (cfg, MONO_LVREG_LS (ins->dreg));
14487 tree->opcode = OP_REGOFFSET;
14488 tree->inst_basereg = ins->inst_basereg;
14489 tree->inst_offset = ins->inst_offset + MINI_LS_WORD_OFFSET;
14491 tree = get_vreg_to_inst (cfg, MONO_LVREG_MS (ins->dreg));
14493 tree->opcode = OP_REGOFFSET;
14494 tree->inst_basereg = ins->inst_basereg;
14495 tree->inst_offset = ins->inst_offset + MINI_MS_WORD_OFFSET;
14505 if (cfg->compute_gc_maps) {
14506 /* registers need liveness info even for !non refs */
14507 for (i = 0; i < cfg->num_varinfo; i++) {
14508 MonoInst *ins = cfg->varinfo [i];
14510 if (ins->opcode == OP_REGVAR)
14511 ins->flags |= MONO_INST_GC_TRACK;
14515 /* FIXME: widening and truncation */
14518 * As an optimization, when a variable allocated to the stack is first loaded into
14519 * an lvreg, we will remember the lvreg and use it the next time instead of loading
14520 * the variable again.
14522 orig_next_vreg = cfg->next_vreg;
14523 vreg_to_lvreg = (guint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
14524 lvregs = (guint32 *)mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024);
14528 * These arrays contain the first and last instructions accessing a given
14530 * Since we emit bblocks in the same order we process them here, and we
14531 * don't split live ranges, these will precisely describe the live range of
14532 * the variable, i.e. the instruction range where a valid value can be found
14533 * in the variables location.
14534 * The live range is computed using the liveness info computed by the liveness pass.
14535 * We can't use vmv->range, since that is an abstract live range, and we need
14536 * one which is instruction precise.
14537 * FIXME: Variables used in out-of-line bblocks have a hole in their live range.
14539 /* FIXME: Only do this if debugging info is requested */
14540 live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
14541 live_range_end = g_new0 (MonoInst*, cfg->next_vreg);
14542 live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
14543 live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
14545 /* Add spill loads/stores */
14546 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
14549 if (cfg->verbose_level > 2)
14550 printf ("\nSPILL BLOCK %d:\n", bb->block_num);
14552 /* Clear vreg_to_lvreg array */
14553 for (i = 0; i < lvregs_len; i++)
14554 vreg_to_lvreg [lvregs [i]] = 0;
14558 MONO_BB_FOR_EACH_INS (bb, ins) {
14559 const char *spec = INS_INFO (ins->opcode);
14560 int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs;
14561 gboolean store, no_lvreg;
14562 int sregs [MONO_MAX_SRC_REGS];
14564 if (G_UNLIKELY (cfg->verbose_level > 2))
14565 mono_print_ins (ins);
14567 if (ins->opcode == OP_NOP)
14571 * We handle LDADDR here as well, since it can only be decomposed
14572 * when variable addresses are known.
14574 if (ins->opcode == OP_LDADDR) {
14575 MonoInst *var = (MonoInst *)ins->inst_p0;
14577 if (var->opcode == OP_VTARG_ADDR) {
14578 /* Happens on SPARC/S390 where vtypes are passed by reference */
14579 MonoInst *vtaddr = var->inst_left;
14580 if (vtaddr->opcode == OP_REGVAR) {
14581 ins->opcode = OP_MOVE;
14582 ins->sreg1 = vtaddr->dreg;
14584 else if (var->inst_left->opcode == OP_REGOFFSET) {
14585 ins->opcode = OP_LOAD_MEMBASE;
14586 ins->inst_basereg = vtaddr->inst_basereg;
14587 ins->inst_offset = vtaddr->inst_offset;
14590 } else if (cfg->gsharedvt && cfg->gsharedvt_vreg_to_idx [var->dreg] < 0) {
14591 /* gsharedvt arg passed by ref */
14592 g_assert (var->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
14594 ins->opcode = OP_LOAD_MEMBASE;
14595 ins->inst_basereg = var->inst_basereg;
14596 ins->inst_offset = var->inst_offset;
14597 } else if (cfg->gsharedvt && cfg->gsharedvt_vreg_to_idx [var->dreg]) {
14598 MonoInst *load, *load2, *load3;
14599 int idx = cfg->gsharedvt_vreg_to_idx [var->dreg] - 1;
14600 int reg1, reg2, reg3;
14601 MonoInst *info_var = cfg->gsharedvt_info_var;
14602 MonoInst *locals_var = cfg->gsharedvt_locals_var;
14606 * Compute the address of the local as gsharedvt_locals_var + gsharedvt_info_var->locals_offsets [idx].
14609 g_assert (var->opcode == OP_GSHAREDVT_LOCAL);
14611 g_assert (info_var);
14612 g_assert (locals_var);
14614 /* Mark the instruction used to compute the locals var as used */
14615 cfg->gsharedvt_locals_var_ins = NULL;
14617 /* Load the offset */
14618 if (info_var->opcode == OP_REGOFFSET) {
14619 reg1 = alloc_ireg (cfg);
14620 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, reg1, info_var->inst_basereg, info_var->inst_offset);
14621 } else if (info_var->opcode == OP_REGVAR) {
14623 reg1 = info_var->dreg;
14625 g_assert_not_reached ();
14627 reg2 = alloc_ireg (cfg);
14628 NEW_LOAD_MEMBASE (cfg, load2, OP_LOADI4_MEMBASE, reg2, reg1, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
14629 /* Load the locals area address */
14630 reg3 = alloc_ireg (cfg);
14631 if (locals_var->opcode == OP_REGOFFSET) {
14632 NEW_LOAD_MEMBASE (cfg, load3, OP_LOAD_MEMBASE, reg3, locals_var->inst_basereg, locals_var->inst_offset);
14633 } else if (locals_var->opcode == OP_REGVAR) {
14634 NEW_UNALU (cfg, load3, OP_MOVE, reg3, locals_var->dreg);
14636 g_assert_not_reached ();
14638 /* Compute the address */
14639 ins->opcode = OP_PADD;
14643 mono_bblock_insert_before_ins (bb, ins, load3);
14644 mono_bblock_insert_before_ins (bb, load3, load2);
14646 mono_bblock_insert_before_ins (bb, load2, load);
14648 g_assert (var->opcode == OP_REGOFFSET);
14650 ins->opcode = OP_ADD_IMM;
14651 ins->sreg1 = var->inst_basereg;
14652 ins->inst_imm = var->inst_offset;
14655 *need_local_opts = TRUE;
14656 spec = INS_INFO (ins->opcode);
14659 if (ins->opcode < MONO_CEE_LAST) {
14660 mono_print_ins (ins);
14661 g_assert_not_reached ();
14665 * Store opcodes have destbasereg in the dreg, but in reality, it is an
14669 if (MONO_IS_STORE_MEMBASE (ins)) {
14670 tmp_reg = ins->dreg;
14671 ins->dreg = ins->sreg2;
14672 ins->sreg2 = tmp_reg;
14675 spec2 [MONO_INST_DEST] = ' ';
14676 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14677 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14678 spec2 [MONO_INST_SRC3] = ' ';
14680 } else if (MONO_IS_STORE_MEMINDEX (ins))
14681 g_assert_not_reached ();
14686 if (G_UNLIKELY (cfg->verbose_level > 2)) {
14687 printf ("\t %.3s %d", spec, ins->dreg);
14688 num_sregs = mono_inst_get_src_registers (ins, sregs);
14689 for (srcindex = 0; srcindex < num_sregs; ++srcindex)
14690 printf (" %d", sregs [srcindex]);
14697 regtype = spec [MONO_INST_DEST];
14698 g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' ')));
14701 if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) {
14702 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
14703 MonoInst *store_ins;
14705 MonoInst *def_ins = ins;
14706 int dreg = ins->dreg; /* The original vreg */
14708 store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype);
14710 if (var->opcode == OP_REGVAR) {
14711 ins->dreg = var->dreg;
14712 } 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)) {
14714 * Instead of emitting a load+store, use a _membase opcode.
14716 g_assert (var->opcode == OP_REGOFFSET);
14717 if (ins->opcode == OP_MOVE) {
14721 ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode);
14722 ins->inst_basereg = var->inst_basereg;
14723 ins->inst_offset = var->inst_offset;
14726 spec = INS_INFO (ins->opcode);
14730 g_assert (var->opcode == OP_REGOFFSET);
14732 prev_dreg = ins->dreg;
14734 /* Invalidate any previous lvreg for this vreg */
14735 vreg_to_lvreg [ins->dreg] = 0;
14739 if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) {
14741 store_opcode = OP_STOREI8_MEMBASE_REG;
14744 ins->dreg = alloc_dreg (cfg, stacktypes [regtype]);
14746 #if SIZEOF_REGISTER != 8
14747 if (regtype == 'l') {
14748 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));
14749 mono_bblock_insert_after_ins (bb, ins, store_ins);
14750 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));
14751 mono_bblock_insert_after_ins (bb, ins, store_ins);
14752 def_ins = store_ins;
14757 g_assert (store_opcode != OP_STOREV_MEMBASE);
14759 /* Try to fuse the store into the instruction itself */
14760 /* FIXME: Add more instructions */
14761 if (!lvreg && ((ins->opcode == OP_ICONST) || ((ins->opcode == OP_I8CONST) && (ins->inst_c0 == 0)))) {
14762 ins->opcode = store_membase_reg_to_store_membase_imm (store_opcode);
14763 ins->inst_imm = ins->inst_c0;
14764 ins->inst_destbasereg = var->inst_basereg;
14765 ins->inst_offset = var->inst_offset;
14766 spec = INS_INFO (ins->opcode);
14767 } else if (!lvreg && ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE) || (ins->opcode == OP_LMOVE) || (ins->opcode == OP_RMOVE))) {
14768 ins->opcode = store_opcode;
14769 ins->inst_destbasereg = var->inst_basereg;
14770 ins->inst_offset = var->inst_offset;
14774 tmp_reg = ins->dreg;
14775 ins->dreg = ins->sreg2;
14776 ins->sreg2 = tmp_reg;
14779 spec2 [MONO_INST_DEST] = ' ';
14780 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14781 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14782 spec2 [MONO_INST_SRC3] = ' ';
14784 } else if (!lvreg && (op_to_op_store_membase (store_opcode, ins->opcode) != -1)) {
14785 // FIXME: The backends expect the base reg to be in inst_basereg
14786 ins->opcode = op_to_op_store_membase (store_opcode, ins->opcode);
14788 ins->inst_basereg = var->inst_basereg;
14789 ins->inst_offset = var->inst_offset;
14790 spec = INS_INFO (ins->opcode);
14792 /* printf ("INS: "); mono_print_ins (ins); */
14793 /* Create a store instruction */
14794 NEW_STORE_MEMBASE (cfg, store_ins, store_opcode, var->inst_basereg, var->inst_offset, ins->dreg);
14796 /* Insert it after the instruction */
14797 mono_bblock_insert_after_ins (bb, ins, store_ins);
14799 def_ins = store_ins;
14802 * We can't assign ins->dreg to var->dreg here, since the
14803 * sregs could use it. So set a flag, and do it after
14806 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)))
14807 dest_has_lvreg = TRUE;
14812 if (def_ins && !live_range_start [dreg]) {
14813 live_range_start [dreg] = def_ins;
14814 live_range_start_bb [dreg] = bb;
14817 if (cfg->compute_gc_maps && def_ins && (var->flags & MONO_INST_GC_TRACK)) {
14820 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_DEF);
14821 tmp->inst_c1 = dreg;
14822 mono_bblock_insert_after_ins (bb, def_ins, tmp);
14829 num_sregs = mono_inst_get_src_registers (ins, sregs);
14830 for (srcindex = 0; srcindex < 3; ++srcindex) {
14831 regtype = spec [MONO_INST_SRC1 + srcindex];
14832 sreg = sregs [srcindex];
14834 g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
14835 if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
14836 MonoInst *var = get_vreg_to_inst (cfg, sreg);
14837 MonoInst *use_ins = ins;
14838 MonoInst *load_ins;
14839 guint32 load_opcode;
14841 if (var->opcode == OP_REGVAR) {
14842 sregs [srcindex] = var->dreg;
14843 //mono_inst_set_src_registers (ins, sregs);
14844 live_range_end [sreg] = use_ins;
14845 live_range_end_bb [sreg] = bb;
14847 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14850 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14851 /* var->dreg is a hreg */
14852 tmp->inst_c1 = sreg;
14853 mono_bblock_insert_after_ins (bb, ins, tmp);
14859 g_assert (var->opcode == OP_REGOFFSET);
14861 load_opcode = mono_type_to_load_membase (cfg, var->inst_vtype);
14863 g_assert (load_opcode != OP_LOADV_MEMBASE);
14865 if (vreg_to_lvreg [sreg]) {
14866 g_assert (vreg_to_lvreg [sreg] != -1);
14868 /* The variable is already loaded to an lvreg */
14869 if (G_UNLIKELY (cfg->verbose_level > 2))
14870 printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg);
14871 sregs [srcindex] = vreg_to_lvreg [sreg];
14872 //mono_inst_set_src_registers (ins, sregs);
14876 /* Try to fuse the load into the instruction */
14877 if ((srcindex == 0) && (op_to_op_src1_membase (cfg, load_opcode, ins->opcode) != -1)) {
14878 ins->opcode = op_to_op_src1_membase (cfg, load_opcode, ins->opcode);
14879 sregs [0] = var->inst_basereg;
14880 //mono_inst_set_src_registers (ins, sregs);
14881 ins->inst_offset = var->inst_offset;
14882 } else if ((srcindex == 1) && (op_to_op_src2_membase (cfg, load_opcode, ins->opcode) != -1)) {
14883 ins->opcode = op_to_op_src2_membase (cfg, load_opcode, ins->opcode);
14884 sregs [1] = var->inst_basereg;
14885 //mono_inst_set_src_registers (ins, sregs);
14886 ins->inst_offset = var->inst_offset;
14888 if (MONO_IS_REAL_MOVE (ins)) {
14889 ins->opcode = OP_NOP;
14892 //printf ("%d ", srcindex); mono_print_ins (ins);
14894 sreg = alloc_dreg (cfg, stacktypes [regtype]);
14896 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) {
14897 if (var->dreg == prev_dreg) {
14899 * sreg refers to the value loaded by the load
14900 * emitted below, but we need to use ins->dreg
14901 * since it refers to the store emitted earlier.
14905 g_assert (sreg != -1);
14906 vreg_to_lvreg [var->dreg] = sreg;
14907 g_assert (lvregs_len < 1024);
14908 lvregs [lvregs_len ++] = var->dreg;
14912 sregs [srcindex] = sreg;
14913 //mono_inst_set_src_registers (ins, sregs);
14915 #if SIZEOF_REGISTER != 8
14916 if (regtype == 'l') {
14917 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, MONO_LVREG_MS (sreg), var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET);
14918 mono_bblock_insert_before_ins (bb, ins, load_ins);
14919 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, MONO_LVREG_LS (sreg), var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET);
14920 mono_bblock_insert_before_ins (bb, ins, load_ins);
14921 use_ins = load_ins;
14926 #if SIZEOF_REGISTER == 4
14927 g_assert (load_opcode != OP_LOADI8_MEMBASE);
14929 NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset);
14930 mono_bblock_insert_before_ins (bb, ins, load_ins);
14931 use_ins = load_ins;
14935 if (var->dreg < orig_next_vreg) {
14936 live_range_end [var->dreg] = use_ins;
14937 live_range_end_bb [var->dreg] = bb;
14940 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14943 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14944 tmp->inst_c1 = var->dreg;
14945 mono_bblock_insert_after_ins (bb, ins, tmp);
14949 mono_inst_set_src_registers (ins, sregs);
14951 if (dest_has_lvreg) {
14952 g_assert (ins->dreg != -1);
14953 vreg_to_lvreg [prev_dreg] = ins->dreg;
14954 g_assert (lvregs_len < 1024);
14955 lvregs [lvregs_len ++] = prev_dreg;
14956 dest_has_lvreg = FALSE;
14960 tmp_reg = ins->dreg;
14961 ins->dreg = ins->sreg2;
14962 ins->sreg2 = tmp_reg;
14965 if (MONO_IS_CALL (ins)) {
14966 /* Clear vreg_to_lvreg array */
14967 for (i = 0; i < lvregs_len; i++)
14968 vreg_to_lvreg [lvregs [i]] = 0;
14970 } else if (ins->opcode == OP_NOP) {
14972 MONO_INST_NULLIFY_SREGS (ins);
14975 if (cfg->verbose_level > 2)
14976 mono_print_ins_index (1, ins);
14979 /* Extend the live range based on the liveness info */
14980 if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) {
14981 for (i = 0; i < cfg->num_varinfo; i ++) {
14982 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
14984 if (vreg_is_volatile (cfg, vi->vreg))
14985 /* The liveness info is incomplete */
14988 if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) {
14989 /* Live from at least the first ins of this bb */
14990 live_range_start [vi->vreg] = bb->code;
14991 live_range_start_bb [vi->vreg] = bb;
14994 if (mono_bitset_test_fast (bb->live_out_set, i)) {
14995 /* Live at least until the last ins of this bb */
14996 live_range_end [vi->vreg] = bb->last_ins;
14997 live_range_end_bb [vi->vreg] = bb;
15004 * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
15005 * by storing the current native offset into MonoMethodVar->live_range_start/end.
15007 if (cfg->backend->have_liverange_ops && cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) {
15008 for (i = 0; i < cfg->num_varinfo; ++i) {
15009 int vreg = MONO_VARINFO (cfg, i)->vreg;
15012 if (live_range_start [vreg]) {
15013 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
15015 ins->inst_c1 = vreg;
15016 mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
15018 if (live_range_end [vreg]) {
15019 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
15021 ins->inst_c1 = vreg;
15022 if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins)
15023 mono_add_ins_to_end (live_range_end_bb [vreg], ins);
15025 mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
15030 if (cfg->gsharedvt_locals_var_ins) {
15031 /* Nullify if unused */
15032 cfg->gsharedvt_locals_var_ins->opcode = OP_PCONST;
15033 cfg->gsharedvt_locals_var_ins->inst_imm = 0;
15036 g_free (live_range_start);
15037 g_free (live_range_end);
15038 g_free (live_range_start_bb);
15039 g_free (live_range_end_bb);
15044 * - use 'iadd' instead of 'int_add'
15045 * - handling ovf opcodes: decompose in method_to_ir.
15046 * - unify iregs/fregs
15047 * -> partly done, the missing parts are:
15048 * - a more complete unification would involve unifying the hregs as well, so
15049 * code wouldn't need if (fp) all over the place. but that would mean the hregs
15050 * would no longer map to the machine hregs, so the code generators would need to
15051 * be modified. Also, on ia64 for example, niregs + nfregs > 256 -> bitmasks
15052 * wouldn't work any more. Duplicating the code in mono_local_regalloc () into
15053 * fp/non-fp branches speeds it up by about 15%.
15054 * - use sext/zext opcodes instead of shifts
15056 * - get rid of TEMPLOADs if possible and use vregs instead
15057 * - clean up usage of OP_P/OP_ opcodes
15058 * - cleanup usage of DUMMY_USE
15059 * - cleanup the setting of ins->type for MonoInst's which are pushed on the
15061 * - set the stack type and allocate a dreg in the EMIT_NEW macros
15062 * - get rid of all the <foo>2 stuff when the new JIT is ready.
15063 * - make sure handle_stack_args () is called before the branch is emitted
15064 * - when the new IR is done, get rid of all unused stuff
15065 * - COMPARE/BEQ as separate instructions or unify them ?
15066 * - keeping them separate allows specialized compare instructions like
15067 * compare_imm, compare_membase
15068 * - most back ends unify fp compare+branch, fp compare+ceq
15069 * - integrate mono_save_args into inline_method
15070 * - get rid of the empty bblocks created by MONO_EMIT_NEW_BRACH_BLOCK2
15071 * - handle long shift opts on 32 bit platforms somehow: they require
15072 * 3 sregs (2 for arg1 and 1 for arg2)
15073 * - make byref a 'normal' type.
15074 * - use vregs for bb->out_stacks if possible, handle_global_vreg will make them a
15075 * variable if needed.
15076 * - do not start a new IL level bblock when cfg->cbb is changed by a function call
15077 * like inline_method.
15078 * - remove inlining restrictions
15079 * - fix LNEG and enable cfold of INEG
15080 * - generalize x86 optimizations like ldelema as a peephole optimization
15081 * - add store_mem_imm for amd64
15082 * - optimize the loading of the interruption flag in the managed->native wrappers
15083 * - avoid special handling of OP_NOP in passes
15084 * - move code inserting instructions into one function/macro.
15085 * - try a coalescing phase after liveness analysis
15086 * - add float -> vreg conversion + local optimizations on !x86
15087 * - figure out how to handle decomposed branches during optimizations, ie.
15088 * compare+branch, op_jump_table+op_br etc.
15089 * - promote RuntimeXHandles to vregs
15090 * - vtype cleanups:
15091 * - add a NEW_VARLOADA_VREG macro
15092 * - the vtype optimizations are blocked by the LDADDR opcodes generated for
15093 * accessing vtype fields.
15094 * - get rid of I8CONST on 64 bit platforms
15095 * - dealing with the increase in code size due to branches created during opcode
15097 * - use extended basic blocks
15098 * - all parts of the JIT
15099 * - handle_global_vregs () && local regalloc
15100 * - avoid introducing global vregs during decomposition, like 'vtable' in isinst
15101 * - sources of increase in code size:
15104 * - isinst and castclass
15105 * - lvregs not allocated to global registers even if used multiple times
15106 * - call cctors outside the JIT, to make -v output more readable and JIT timings more
15108 * - check for fp stack leakage in other opcodes too. (-> 'exceptions' optimization)
15109 * - add all micro optimizations from the old JIT
15110 * - put tree optimizations into the deadce pass
15111 * - decompose op_start_handler/op_endfilter/op_endfinally earlier using an arch
15112 * specific function.
15113 * - unify the float comparison opcodes with the other comparison opcodes, i.e.
15114 * fcompare + branchCC.
15115 * - create a helper function for allocating a stack slot, taking into account
15116 * MONO_CFG_HAS_SPILLUP.
15118 * - merge the ia64 switch changes.
15119 * - optimize mono_regstate2_alloc_int/float.
15120 * - fix the pessimistic handling of variables accessed in exception handler blocks.
15121 * - need to write a tree optimization pass, but the creation of trees is difficult, i.e.
15122 * parts of the tree could be separated by other instructions, killing the tree
15123 * arguments, or stores killing loads etc. Also, should we fold loads into other
15124 * instructions if the result of the load is used multiple times ?
15125 * - make the REM_IMM optimization in mini-x86.c arch-independent.
15126 * - LAST MERGE: 108395.
15127 * - when returning vtypes in registers, generate IR and append it to the end of the
15128 * last bb instead of doing it in the epilog.
15129 * - change the store opcodes so they use sreg1 instead of dreg to store the base register.
15137 - When to decompose opcodes:
15138 - earlier: this makes some optimizations hard to implement, since the low level IR
15139 no longer contains the neccessary information. But it is easier to do.
15140 - later: harder to implement, enables more optimizations.
15141 - Branches inside bblocks:
15142 - created when decomposing complex opcodes.
15143 - branches to another bblock: harmless, but not tracked by the branch
15144 optimizations, so need to branch to a label at the start of the bblock.
15145 - branches to inside the same bblock: very problematic, trips up the local
15146 reg allocator. Can be fixed by spitting the current bblock, but that is a
15147 complex operation, since some local vregs can become global vregs etc.
15148 - Local/global vregs:
15149 - local vregs: temporary vregs used inside one bblock. Assigned to hregs by the
15150 local register allocator.
15151 - global vregs: used in more than one bblock. Have an associated MonoMethodVar
15152 structure, created by mono_create_var (). Assigned to hregs or the stack by
15153 the global register allocator.
15154 - When to do optimizations like alu->alu_imm:
15155 - earlier -> saves work later on since the IR will be smaller/simpler
15156 - later -> can work on more instructions
15157 - Handling of valuetypes:
15158 - When a vtype is pushed on the stack, a new temporary is created, an
15159 instruction computing its address (LDADDR) is emitted and pushed on
15160 the stack. Need to optimize cases when the vtype is used immediately as in
15161 argument passing, stloc etc.
15162 - Instead of the to_end stuff in the old JIT, simply call the function handling
15163 the values on the stack before emitting the last instruction of the bb.
15166 #endif /* DISABLE_JIT */