X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmethod-to-ir.c;h=18e1483945b212798d7baa1bacebd29b398c12f1;hb=9ab38c6192912cc1efb8178d8cd5fe00d7c90a78;hp=59d2aa68bf2894ae61b2e74495da435fbeb539ae;hpb=a2d68ee221fbae2272b995b4f801ff69b5d6e6b1;p=mono.git diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index 59d2aa68bf2..18e1483945b 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -23,6 +23,10 @@ #include #endif +#ifdef HAVE_ALLOCA_H +#include +#endif + #ifdef HAVE_VALGRIND_MEMCHECK_H #include #endif @@ -117,13 +121,17 @@ extern MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline; #ifdef MINI_OP #undef MINI_OP #endif -#define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, +#ifdef MINI_OP3 +#undef MINI_OP3 +#endif +#define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ', +#define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3, #define NONE ' ' #define IREG 'i' #define FREG 'f' #define VREG 'v' #define XREG 'x' -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 #define LREG IREG #else #define LREG 'l' @@ -134,6 +142,19 @@ ins_info[] = { #include "mini-ops.h" }; #undef MINI_OP +#undef MINI_OP3 + +#define MINI_OP(a,b,dest,src1,src2) ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0)), +#define MINI_OP3(a,b,dest,src1,src2,src3) ((src3) != NONE ? 3 : ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0))), +/* + * This should contain the index of the last sreg + 1. This is not the same + * as the number of sregs for opcodes like IA64_CMP_EQ_IMM. + */ +const gint8 ins_sreg_counts[] = { +#include "mini-ops.h" +}; +#undef MINI_OP +#undef MINI_OP3 extern GHashTable *jit_icall_name_hash; @@ -143,6 +164,14 @@ extern GHashTable *jit_icall_name_hash; (vi)->idx = (id); \ } while (0) +void +mono_inst_set_src_registers (MonoInst *ins, int *regs) +{ + ins->sreg1 = regs [0]; + ins->sreg2 = regs [1]; + ins->sreg3 = regs [2]; +} + guint32 mono_alloc_ireg (MonoCompile *cfg) { @@ -199,7 +228,7 @@ handle_enum: return OP_MOVE; case MONO_TYPE_I8: case MONO_TYPE_U8: -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 return OP_MOVE; #else return OP_LMOVE; @@ -210,7 +239,7 @@ handle_enum: return OP_FMOVE; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { - type = type->data.klass->enum_basetype; + type = mono_class_enum_basetype (type->data.klass); goto handle_enum; } if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type))) @@ -266,7 +295,7 @@ mono_print_bb (MonoBasicBlock *bb, const char *msg) } \ } while (0) -#if defined(__i386__) || defined(__x86_64__) +#if defined(TARGET_X86) || defined(TARGET_AMD64) #define EMIT_NEW_X86_LEA(cfg,dest,sr1,sr2,shift,imm) do { \ MONO_INST_NEW (cfg, dest, OP_X86_LEA); \ (dest)->dreg = alloc_preg ((cfg)); \ @@ -278,7 +307,7 @@ mono_print_bb (MonoBasicBlock *bb, const char *msg) } while (0) #endif -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 #define ADD_WIDEN_OP(ins, arg1, arg2) do { \ /* FIXME: Need to add many more cases */ \ if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) { \ @@ -303,8 +332,7 @@ mono_print_bb (MonoBasicBlock *bb, const char *msg) ADD_WIDEN_OP (ins, sp [0], sp [1]); \ ins->dreg = alloc_dreg ((cfg), (ins)->type); \ MONO_ADD_INS ((cfg)->cbb, (ins)); \ - *sp++ = ins; \ - mono_decompose_opcode ((cfg), (ins)); \ + *sp++ = mono_decompose_opcode ((cfg), (ins)); \ } while (0) #define ADD_UNOP(op) do { \ @@ -315,8 +343,7 @@ mono_print_bb (MonoBasicBlock *bb, const char *msg) CHECK_TYPE (ins); \ (ins)->dreg = alloc_dreg ((cfg), (ins)->type); \ MONO_ADD_INS ((cfg)->cbb, (ins)); \ - *sp++ = ins; \ - mono_decompose_opcode (cfg, ins); \ + *sp++ = mono_decompose_opcode (cfg, ins); \ } while (0) #define ADD_BINCOND(next_block) do { \ @@ -440,7 +467,6 @@ mono_find_block_region (MonoCompile *cfg, int offset) MonoExceptionClause *clause; int i; - /* first search for handlers and filters */ for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) && @@ -455,11 +481,7 @@ mono_find_block_region (MonoCompile *cfg, int offset) else return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags; } - } - /* search the try blocks */ - for (i = 0; i < header->num_clauses; ++i) { - clause = &header->clauses [i]; if (MONO_OFFSET_IN_CLAUSE (clause, offset)) return ((i + 1) << 8) | clause->flags; } @@ -584,7 +606,7 @@ handle_enum: return; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { - type = type->data.klass->enum_basetype; + type = mono_class_enum_basetype (type->data.klass); goto handle_enum; } else { inst->klass = klass; @@ -757,7 +779,7 @@ type_from_op (MonoInst *ins, MonoInst *src1, MonoInst *src2) { case OP_LCOMPARE: case OP_ICOMPARE: ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV; - if ((src1->type == STACK_I8) || ((sizeof (gpointer) == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP)))) + if ((src1->type == STACK_I8) || ((SIZEOF_REGISTER == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP)))) ins->opcode = OP_LCOMPARE; else if (src1->type == STACK_R8) ins->opcode = OP_FCOMPARE; @@ -766,7 +788,7 @@ type_from_op (MonoInst *ins, MonoInst *src1, MonoInst *src2) { break; case OP_ICOMPARE_IMM: ins->type = bin_comp_table [src1->type] [src1->type] ? STACK_I4 : STACK_INV; - if ((src1->type == STACK_I8) || ((sizeof (gpointer) == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP)))) + if ((src1->type == STACK_I8) || ((SIZEOF_REGISTER == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP)))) ins->opcode = OP_LCOMPARE_IMM; break; case CEE_BEQ: @@ -854,7 +876,7 @@ type_from_op (MonoInst *ins, MonoInst *src1, MonoInst *src2) { break; case STACK_PTR: case STACK_MP: -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 ins->opcode = OP_LCONV_TO_U; #else ins->opcode = OP_MOVE; @@ -1607,24 +1629,24 @@ mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, switch (size) { case 1: MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val); - break; + return; case 2: MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val); - break; + return; case 4: MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val); - break; -#if SIZEOF_VOID_P == 8 + return; +#if SIZEOF_REGISTER == 8 case 8: MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val); + return; #endif } - return; } val_reg = alloc_preg (cfg); - if (sizeof (gpointer) == 8) + if (SIZEOF_REGISTER == 8) MONO_EMIT_NEW_I8CONST (cfg, val_reg, val); else MONO_EMIT_NEW_ICONST (cfg, val_reg, val); @@ -1640,7 +1662,7 @@ mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, } #if !NO_UNALIGNED_ACCESS - if (sizeof (gpointer) == 8) { + if (SIZEOF_REGISTER == 8) { if (offset % 8) { MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg); offset += 4; @@ -1694,7 +1716,7 @@ mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int so } #if !NO_UNALIGNED_ACCESS - if (sizeof (gpointer) == 8) { + if (SIZEOF_REGISTER == 8) { while (size >= 8) { cur_reg = alloc_preg (cfg); MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset); @@ -1773,7 +1795,7 @@ handle_enum: return calli? OP_FCALL_REG: virt? OP_FCALLVIRT: OP_FCALL; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { - type = type->data.klass->enum_basetype; + type = mono_class_enum_basetype (type->data.klass); goto handle_enum; } else return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL; @@ -1974,7 +1996,7 @@ handle_enum: continue; case MONO_TYPE_VALUETYPE: if (simple_type->data.klass->enumtype) { - simple_type = simple_type->data.klass->enum_basetype; + simple_type = mono_class_enum_basetype (simple_type->data.klass); goto handle_enum; } if (args [i]->type != STACK_VTYPE) @@ -2151,7 +2173,14 @@ mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig, } #endif +#ifdef ENABLE_LLVM + if (COMPILE_LLVM (cfg)) + mono_llvm_emit_call (cfg, call); + else + mono_arch_emit_call (cfg, call); +#else mono_arch_emit_call (cfg, call); +#endif cfg->param_area = MAX (cfg->param_area, call->stack_usage); cfg->flags |= MONO_CFG_HAS_CALLS; @@ -2176,7 +2205,7 @@ mono_emit_rgctx_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **ar { #ifdef MONO_ARCH_RGCTX_REG MonoCallInst *call; - int rgctx_reg; + int rgctx_reg = -1; if (rgctx_arg) { rgctx_reg = mono_alloc_preg (cfg); @@ -2186,6 +2215,7 @@ mono_emit_rgctx_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **ar if (rgctx_arg) { mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE); cfg->uses_rgctx_reg = TRUE; + call->rgctx_reg = TRUE; } return (MonoInst*)call; #else @@ -2205,7 +2235,7 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign if (method->string_ctor) { /* Create the real signature */ /* FIXME: Cache these */ - MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_full (cfg->mempool, sig); + MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig); ctor_sig->ret = &mono_defaults.string_class->byval_arg; sig = ctor_sig; @@ -2228,9 +2258,21 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign this_reg = this->dreg; +#ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE + if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) { + /* Make a call to delegate->invoke_impl */ + call->inst.opcode = callvirt_to_call_membase (call->inst.opcode); + call->inst.inst_basereg = this_reg; + call->inst.inst_offset = G_STRUCT_OFFSET (MonoDelegate, invoke_impl); + MONO_ADD_INS (cfg->cbb, (MonoInst*)call); + + return (MonoInst*)call; + } +#endif + if ((!cfg->compile_aot || enable_for_aot) && (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) || - ((method->flags & METHOD_ATTRIBUTE_FINAL) && + (MONO_METHOD_IS_FINAL (method) && method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK))) { /* * the method is not virtual, we just need to ensure this is not null @@ -2253,21 +2295,7 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign return (MonoInst*)call; } -#ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE - if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) { - /* Make a call to delegate->invoke_impl */ - call->inst.opcode = callvirt_to_call_membase (call->inst.opcode); - call->inst.inst_basereg = this_reg; - call->inst.inst_offset = G_STRUCT_OFFSET (MonoDelegate, invoke_impl); - MONO_ADD_INS (cfg->cbb, (MonoInst*)call); - - return (MonoInst*)call; - } -#endif - - if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && - ((method->flags & METHOD_ATTRIBUTE_FINAL) || - (method->klass && method->klass->flags & TYPE_ATTRIBUTE_SEALED))) { + if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && MONO_METHOD_IS_FINAL (method)) { /* * the method is virtual, but we can statically dispatch since either * it's class or the method itself are sealed. @@ -2346,6 +2374,7 @@ mono_emit_rgctx_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMeth #ifdef MONO_ARCH_RGCTX_REG mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE); cfg->uses_rgctx_reg = TRUE; + call->rgctx_reg = TRUE; #else NOT_IMPLEMENTED; #endif @@ -2409,6 +2438,48 @@ mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer ((MonoCallInst*)ins)->fptr_is_patch = TRUE; return ins; } + +static MonoInst* +mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig) +{ + if (!MONO_TYPE_IS_VOID (fsig->ret)) { + if ((fsig->pinvoke || LLVM_ENABLED) && !fsig->ret->byref) { + int widen_op = -1; + + /* + * Native code might return non register sized integers + * without initializing the upper bits. + */ + switch (mono_type_to_load_membase (cfg, fsig->ret)) { + case OP_LOADI1_MEMBASE: + widen_op = OP_ICONV_TO_I1; + break; + case OP_LOADU1_MEMBASE: + widen_op = OP_ICONV_TO_U1; + break; + case OP_LOADI2_MEMBASE: + widen_op = OP_ICONV_TO_I2; + break; + case OP_LOADU2_MEMBASE: + widen_op = OP_ICONV_TO_U2; + break; + default: + break; + } + + if (widen_op != -1) { + int dreg = alloc_preg (cfg); + MonoInst *widen; + + EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg); + widen->type = ins->type; + ins = widen; + } + } + } + + return ins; +} static MonoMethod* get_memcpy_method (void) @@ -2445,6 +2516,21 @@ mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *kla else n = mono_class_value_size (klass, &align); +#if HAVE_WRITE_BARRIERS + /* if native is true there should be no references in the struct */ + if (klass->has_references && !native) { + /* Avoid barriers when storing to the stack */ + if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) || + (dest->opcode == OP_LDADDR))) { + iargs [0] = dest; + iargs [1] = src; + EMIT_NEW_PCONST (cfg, iargs [2], klass); + + mono_emit_jit_icall (cfg, mono_value_copy, iargs); + } + } +#endif + if ((cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 5) { /* FIXME: Optimize the case when src/dest is OP_LDADDR */ mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, n, align); @@ -2758,7 +2844,7 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box) EMIT_NEW_CLASSCONST (cfg, iargs [1], klass); alloc_ftn = mono_object_new; - } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib) { + } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !klass->generic_class) { /* This happens often in argument checking code, eg. throw new FooException... */ /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */ EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token)); @@ -3244,6 +3330,9 @@ handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip) cfg->flags |= MONO_CFG_HAS_VARARGS; + /* mono_array_new_va () needs a vararg calling convention */ + cfg->disable_llvm = TRUE; + /* FIXME: This uses info->sig, but it should use the signature of the wrapper */ return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp); } @@ -3288,7 +3377,7 @@ static gboolean inline_limit_inited; static gboolean mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method) { - MonoMethodHeader *header = mono_method_get_header (method); + MonoMethodHeader *header; MonoVTable *vtable; #ifdef MONO_ARCH_SOFT_FLOAT MonoMethodSignature *sig = mono_method_signature (method); @@ -3305,6 +3394,12 @@ mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method) return TRUE; #endif + if (method->is_inflated) + /* Avoid inflating the header */ + header = mono_method_get_header (((MonoMethodInflated*)method)->declaring); + else + header = mono_method_get_header (method); + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) || @@ -3420,17 +3515,22 @@ mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, Mono array_reg = arr->dreg; index_reg = index->dreg; -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 /* The array reg is 64 bits but the index reg is only 32 */ index2_reg = alloc_preg (cfg); MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg); #else - index2_reg = index_reg; + if (index->type == STACK_I8) { + index2_reg = alloc_preg (cfg); + MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg); + } else { + index2_reg = index_reg; + } #endif MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg); -#if defined(__i386__) || defined(__x86_64__) +#if defined(TARGET_X86) || defined(TARGET_AMD64) if (size == 1 || size == 2 || size == 4 || size == 8) { static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 }; @@ -3555,7 +3655,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign int mult_reg = alloc_preg (cfg); int add_reg = alloc_preg (cfg); -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 /* The array reg is 64 bits but the index reg is only 32 */ MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index_reg, args [1]->dreg); #else @@ -3563,7 +3663,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign #endif MONO_EMIT_BOUNDS_CHECK (cfg, args [0]->dreg, MonoString, length, index_reg); -#if defined(__i386__) || defined(__x86_64__) +#if defined(TARGET_X86) || defined(TARGET_AMD64) EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, G_STRUCT_OFFSET (MonoString, chars)); add_reg = ins->dreg; /* Avoid a warning */ @@ -3722,7 +3822,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign (strcmp (cmethod->klass->name, "Interlocked") == 0)) { ins = NULL; -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 if (strcmp (cmethod->name, "Read") == 0 && (fsig->params [0]->type == MONO_TYPE_I8)) { /* 64 bit reads are already atomic */ MONO_INST_NEW (cfg, ins, OP_LOADI8_MEMBASE); @@ -3740,7 +3840,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (fsig->params [0]->type == MONO_TYPE_I4) opcode = OP_ATOMIC_ADD_NEW_I4; -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 else if (fsig->params [0]->type == MONO_TYPE_I8) opcode = OP_ATOMIC_ADD_NEW_I8; #endif @@ -3764,7 +3864,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (fsig->params [0]->type == MONO_TYPE_I4) opcode = OP_ATOMIC_ADD_NEW_I4; -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 else if (fsig->params [0]->type == MONO_TYPE_I8) opcode = OP_ATOMIC_ADD_NEW_I8; #endif @@ -3787,7 +3887,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (fsig->params [0]->type == MONO_TYPE_I4) opcode = OP_ATOMIC_ADD_NEW_I4; -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 else if (fsig->params [0]->type == MONO_TYPE_I8) opcode = OP_ATOMIC_ADD_NEW_I8; #endif @@ -3810,7 +3910,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (fsig->params [0]->type == MONO_TYPE_I4) opcode = OP_ATOMIC_EXCHANGE_I4; -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 else if ((fsig->params [0]->type == MONO_TYPE_I8) || (fsig->params [0]->type == MONO_TYPE_I) || (fsig->params [0]->type == MONO_TYPE_OBJECT)) @@ -3847,27 +3947,36 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } #endif /* MONO_ARCH_HAVE_ATOMIC_EXCHANGE */ -#ifdef MONO_ARCH_HAVE_ATOMIC_CAS_IMM - /* - * Can't implement CompareExchange methods this way since they have - * three arguments. We can implement one of the common cases, where the new - * value is a constant. - */ +#ifdef MONO_ARCH_HAVE_ATOMIC_CAS if ((strcmp (cmethod->name, "CompareExchange") == 0)) { - if ((fsig->params [1]->type == MONO_TYPE_I4 || - (sizeof (gpointer) == 4 && fsig->params [1]->type == MONO_TYPE_I)) - && args [2]->opcode == OP_ICONST) { - MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_IMM_I4); + int size = 0; + if (fsig->params [1]->type == MONO_TYPE_I4) + size = 4; + else if (fsig->params [1]->type == MONO_TYPE_I || MONO_TYPE_IS_REFERENCE (fsig->params [1])) + size = sizeof (gpointer); + else if (sizeof (gpointer) == 8 && fsig->params [1]->type == MONO_TYPE_I4) + size = 8; + if (size == 4) { + MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4); ins->dreg = alloc_ireg (cfg); ins->sreg1 = args [0]->dreg; ins->sreg2 = args [1]->dreg; - ins->backend.data = GINT_TO_POINTER (args [2]->inst_c0); + ins->sreg3 = args [2]->dreg; ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); + } else if (size == 8) { + MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I8); + ins->dreg = alloc_ireg (cfg); + ins->sreg1 = args [0]->dreg; + ins->sreg2 = args [1]->dreg; + ins->sreg3 = args [2]->dreg; + ins->type = STACK_I8; + MONO_ADD_INS (cfg->cbb, ins); + } else { + /* g_assert_not_reached (); */ } - /* The I8 case is hard to detect, since the arg might be a conv.i8 (iconst) tree */ } -#endif /* MONO_ARCH_HAVE_ATOMIC_CAS_IMM */ +#endif /* MONO_ARCH_HAVE_ATOMIC_CAS */ if (ins) return ins; @@ -4038,6 +4147,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, guint32 prev_cil_offset_to_bb_len; MonoMethod *prev_current_method; MonoGenericContext *prev_generic_context; + gboolean ret_var_set, prev_ret_var_set; g_assert (cfg->exception_type == MONO_EXCEPTION_NONE); @@ -4091,9 +4201,12 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, prev_cbb = cfg->cbb; prev_current_method = cfg->current_method; prev_generic_context = cfg->generic_context; + prev_ret_var_set = cfg->ret_var_set; costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, dont_inline, sp, real_offset, *ip == CEE_CALLVIRT); + ret_var_set = cfg->ret_var_set; + cfg->inlined_method = prev_inlined_method; cfg->real_offset = prev_real_offset; cfg->cbb_hash = prev_cbb_hash; @@ -4105,6 +4218,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, cfg->arg_types = prev_arg_types; cfg->current_method = prev_current_method; cfg->generic_context = prev_generic_context; + cfg->ret_var_set = prev_ret_var_set; if ((costs >= 0 && costs < 60) || inline_allways) { if (cfg->verbose_level > 2) @@ -4145,7 +4259,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, * If the inlined method contains only a throw, then the ret var is not * set, so set it to a dummy value. */ - if (!cfg->ret_var_set) { + if (!ret_var_set) { static double r8_0 = 0.0; switch (rvar->type) { @@ -4427,55 +4541,85 @@ emit_throw_method_access_exception (MonoCompile *cfg, MonoMethod *caller, MonoMe } static MonoMethod* -verification_exception (void) +field_access_exception (void) { static MonoMethod *method = NULL; if (!method) { MonoSecurityManager *secman = mono_security_manager_get_methods (); method = mono_class_get_method_from_name (secman->securitymanager, - "VerificationException", 0); + "FieldAccessException", 2); } g_assert (method); return method; } static void -emit_throw_verification_exception (MonoCompile *cfg, MonoBasicBlock *bblock, unsigned char *ip) +emit_throw_field_access_exception (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field, + MonoBasicBlock *bblock, unsigned char *ip) +{ + MonoMethod *thrower = field_access_exception (); + MonoInst *args [2]; + + EMIT_NEW_METHODCONST (cfg, args [0], caller); + EMIT_NEW_METHODCONST (cfg, args [1], field); + mono_emit_method_call (cfg, thrower, args, NULL); +} + +/* + * Return the original method is a wrapper is specified. We can only access + * the custom attributes from the original method. + */ +static MonoMethod* +get_original_method (MonoMethod *method) +{ + if (method->wrapper_type == MONO_WRAPPER_NONE) + return method; + + /* native code (which is like Critical) can call any managed method XXX FIXME XXX to validate all usages */ + if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) + return NULL; + + /* in other cases we need to find the original method */ + return mono_marshal_method_from_wrapper (method); +} + +static void +ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field, + MonoBasicBlock *bblock, unsigned char *ip) { - MonoMethod *thrower = verification_exception (); + /* there's no restriction to access Transparent or SafeCritical fields, so we only check calls to Critical methods */ + if (mono_security_core_clr_class_level (mono_field_get_parent (field)) != MONO_SECURITY_CORE_CLR_CRITICAL) + return; + + /* we can't get the coreclr security level on wrappers since they don't have the attributes */ + caller = get_original_method (caller); + if (!caller) + return; - mono_emit_method_call (cfg, thrower, NULL, NULL); + /* caller is Critical! only SafeCritical and Critical callers can access the field, so we throw if caller is Transparent */ + if (mono_security_core_clr_method_level (caller, TRUE) == MONO_SECURITY_CORE_CLR_TRANSPARENT) + emit_throw_field_access_exception (cfg, caller, field, bblock, ip); } static void ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee, MonoBasicBlock *bblock, unsigned char *ip) { - MonoSecurityCoreCLRLevel caller_level = mono_security_core_clr_method_level (caller, TRUE); - MonoSecurityCoreCLRLevel callee_level = mono_security_core_clr_method_level (callee, TRUE); - gboolean is_safe = TRUE; + /* there's no restriction to call Transparent or SafeCritical code, so we only check calls to Critical methods */ + if (mono_security_core_clr_method_level (callee, TRUE) != MONO_SECURITY_CORE_CLR_CRITICAL) + return; - if (!(caller_level >= callee_level || - caller_level == MONO_SECURITY_CORE_CLR_SAFE_CRITICAL || - callee_level == MONO_SECURITY_CORE_CLR_SAFE_CRITICAL)) { - is_safe = FALSE; - } + /* we can't get the coreclr security level on wrappers since they don't have the attributes */ + caller = get_original_method (caller); + if (!caller) + return; - if (!is_safe) + /* caller is Critical! only SafeCritical and Critical callers can call it, so we throw if the caller is Transparent */ + if (mono_security_core_clr_method_level (caller, TRUE) == MONO_SECURITY_CORE_CLR_TRANSPARENT) emit_throw_method_access_exception (cfg, caller, callee, bblock, ip); } -static gboolean -method_is_safe (MonoMethod *method) -{ - /* - if (strcmp (method->name, "unsafeMethod") == 0) - return FALSE; - */ - return TRUE; -} - /* * Check that the IL instructions at ip are the array initialization * sequence and return the pointer to the data and the size. @@ -4930,6 +5074,31 @@ mono_decompose_soft_float (MonoCompile *cfg) restart = TRUE; break; } + case OP_CKFINITE: { + MonoInst *iargs [2]; + MonoInst *call, *cmp; + + /* Convert to icall+icompare+cond_exc+move */ + + /* Create dummy MonoInst's for the arguments */ + MONO_INST_NEW (cfg, iargs [0], OP_ARG); + iargs [0]->dreg = ins->sreg1; + + call = mono_emit_jit_icall (cfg, mono_isfinite, iargs); + + MONO_INST_NEW (cfg, cmp, OP_ICOMPARE_IMM); + cmp->sreg1 = call->dreg; + cmp->inst_imm = 1; + MONO_ADD_INS (cfg->cbb, cmp); + + MONO_EMIT_NEW_COND_EXC (cfg, INE_UN, "ArithmeticException"); + + /* Do the assignment if the value is finite */ + MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, ins->dreg, ins->sreg1); + + restart = TRUE; + break; + } default: if (spec [MONO_INST_SRC1] == 'f' || spec [MONO_INST_SRC2] == 'f' || spec [MONO_INST_DEST] == 'f') { mono_print_ins (ins); @@ -4966,7 +5135,8 @@ emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n) { MonoInst *ins; guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]); - if ((opcode == OP_MOVE) && ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) { + if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] && + ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) { /* Optimize reg-reg moves away */ /* * Can't optimize other opcodes, since sp[0] might point to @@ -5020,6 +5190,17 @@ load_error: return NULL; } +static gboolean +is_exception_class (MonoClass *class) +{ + while (class) { + if (class == mono_defaults.exception_class) + return TRUE; + class = class->parent; + } + return FALSE; +} + /* * mono_method_to_ir: * @@ -5055,6 +5236,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b GSList *class_inits = NULL; gboolean dont_verify, dont_verify_stloc, readonly = FALSE; int context_used; + gboolean init_locals; /* serialization and xdomain stuff may need access to private fields and methods */ dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE; @@ -5080,6 +5262,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cfg->cil_start = ip; end = ip + header->code_size; mono_jit_stats.cil_code_size += header->code_size; + init_locals = header->init_locals; + + /* + * Methods without init_locals set could cause asserts in various passes + * (#497220). + */ + init_locals = TRUE; method_definition = method; while (method_definition->is_inflated) { @@ -5227,9 +5416,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b clause->data.catch_class && cfg->generic_sharing_context && mono_class_check_context_used (clause->data.catch_class)) { - if (mono_method_get_context (method)->method_inst) - GENERIC_SHARING_FAILURE (CEE_NOP); - /* * In shared generic code with catch * clauses containing type variables @@ -5253,7 +5439,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } } else { - arg_array = alloca (sizeof (MonoInst *) * num_args); + arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args); cfg->cbb = start_bblock; cfg->args = arg_array; mono_save_args (cfg, sig, inline_args); @@ -5315,7 +5501,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } - if ((header->init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SHARED))) || cfg->compile_aot || security || pinvoke) { + if ((init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SHARED))) || cfg->compile_aot || security || pinvoke) { /* we use a separate basic block for the initialization code */ NEW_BBLOCK (cfg, init_localsbb); cfg->bb_init = init_localsbb; @@ -5376,8 +5562,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } } - if (!method_is_safe (method)) - emit_throw_verification_exception (cfg, bblock, ip); } if (header->code_size == 0) @@ -5397,6 +5581,22 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } class_inits = NULL; + /* We force the vtable variable here for all shared methods + for the possibility that they might show up in a stack + trace where their exact instantiation is needed. */ + if (cfg->generic_sharing_context && method == cfg->method) { + if ((method->flags & METHOD_ATTRIBUTE_STATIC) || + mini_method_get_context (method)->method_inst || + method->klass->valuetype) { + mono_get_vtable_var (cfg); + } else { + /* FIXME: Is there a better way to do this? + We need the variable live for the duration + of the whole method. */ + cfg->args [0]->flags |= MONO_INST_INDIRECT; + } + } + /* add a check for this != NULL to inlined methods */ if (is_virtual_call) { MonoInst *arg_ins; @@ -5473,7 +5673,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cfg->coverage_info->data [cil_offset].cil_code = ip; /* TODO: Use an increment here */ -#if defined(__i386__) +#if defined(TARGET_X86) MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM); ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count); ins->inst_imm = 1; @@ -5662,9 +5862,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_LDC_R4: { float *f; /* FIXME: we should really allocate this only late in the compilation process */ - mono_domain_lock (cfg->domain); f = mono_domain_alloc (cfg->domain, sizeof (float)); - mono_domain_unlock (cfg->domain); CHECK_OPSIZE (5); CHECK_STACK_OVF (1); MONO_INST_NEW (cfg, ins, OP_R4CONST); @@ -5682,9 +5880,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_LDC_R8: { double *d; /* FIXME: we should really allocate this only late in the compilation process */ - mono_domain_lock (cfg->domain); d = mono_domain_alloc (cfg->domain, sizeof (double)); - mono_domain_unlock (cfg->domain); CHECK_OPSIZE (9); CHECK_STACK_OVF (1); MONO_INST_NEW (cfg, ins, OP_R8CONST); @@ -5724,7 +5920,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ip++; --sp; -#ifdef __i386__ +#ifdef TARGET_X86 if (sp [0]->type == STACK_R8) /* we need to pop the value from the x86 FP stack */ MONO_EMIT_NEW_UNALU (cfg, OP_X86_FPOP, -1, sp [0]->dreg); @@ -5733,6 +5929,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_JMP: { MonoCallInst *call; + INLINE_FAILURE; + CHECK_OPSIZE (5); if (stack_start != sp) UNVERIFIED; @@ -5746,13 +5944,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cfg->generic_sharing_context && mono_method_check_context_used (cmethod)) GENERIC_SHARING_FAILURE (CEE_JMP); - if (mono_security_get_mode () == MONO_SECURITY_MODE_CAS) { - if (check_linkdemand (cfg, method, cmethod)) - INLINE_FAILURE; + if (mono_security_get_mode () == MONO_SECURITY_MODE_CAS) CHECK_CFG_EXCEPTION; - } -#ifdef __x86_64__ +#ifdef TARGET_AMD64 { MonoMethodSignature *fsig = mono_method_signature (cmethod); int i, n; @@ -5823,7 +6018,19 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cmethod = (MonoMethod *)mono_method_get_wrapper_data (method, token); cil_method = cmethod; } else if (constrained_call) { - cmethod = mono_get_method_constrained (image, token, constrained_call, generic_context, &cil_method); + if ((constrained_call->byval_arg.type == MONO_TYPE_VAR || constrained_call->byval_arg.type == MONO_TYPE_MVAR) && cfg->generic_sharing_context) { + /* + * This is needed since get_method_constrained can't find + * the method in klass representing a type var. + * The type var is guaranteed to be a reference type in this + * case. + */ + cmethod = mini_get_method (cfg, method, token, NULL, generic_context); + cil_method = cmethod; + g_assert (!cmethod->klass->valuetype); + } else { + cmethod = mono_get_method_constrained (image, token, constrained_call, generic_context, &cil_method); + } } else { cmethod = mini_get_method (cfg, method, token, NULL, generic_context); cil_method = cmethod; @@ -5878,7 +6085,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b CHECK_CFG_EXCEPTION; } - if (cmethod->string_ctor) + if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) g_assert_not_reached (); } @@ -6011,7 +6218,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } if (!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || - (cmethod->flags & METHOD_ATTRIBUTE_FINAL)) { + MONO_METHOD_IS_FINAL (cmethod)) { if (virtual) check_this = TRUE; virtual = 0; @@ -6037,7 +6244,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* Calling virtual generic methods */ if (cmethod && virtual && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) && - !((cmethod->flags & METHOD_ATTRIBUTE_FINAL) && + !(MONO_METHOD_IS_FINAL (cmethod) && cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) && mono_method_signature (cmethod)->generic_param_count) { MonoInst *this_temp, *this_arg_temp, *store; @@ -6048,20 +6255,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* Prevent inlining of methods that contain indirect calls */ INLINE_FAILURE; -#if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK - if (!(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && - cmethod->wrapper_type == MONO_WRAPPER_NONE) { +#if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK && !defined(ENABLE_LLVM) + if (cmethod->wrapper_type == MONO_WRAPPER_NONE && mono_use_imt) { g_assert (!imt_arg); if (context_used) { imt_arg = emit_get_rgctx_method (cfg, context_used, - cmethod, MONO_RGCTX_INFO_METHOD_CONTEXT); + cmethod, MONO_RGCTX_INFO_METHOD); } else { - // FIXME: - cfg->disable_aot = TRUE; g_assert (cmethod->is_inflated); - EMIT_NEW_PCONST (cfg, imt_arg, - ((MonoMethodInflated*)cmethod)->context.method_inst); + EMIT_NEW_METHODCONST (cfg, imt_arg, cmethod); } ins = mono_emit_method_call_full (cfg, cmethod, fsig, sp, sp [0], imt_arg); } else @@ -6093,7 +6296,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } if (!MONO_TYPE_IS_VOID (fsig->ret)) - *sp++ = ins; + *sp++ = mono_emit_widen_call_res (cfg, ins, fsig); ip += 5; ins_flag = 0; @@ -6104,7 +6307,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* FIXME: runtime generic context pointer for jumps? */ /* FIXME: handle this for generic sharing eventually */ if ((ins_flag & MONO_INST_TAILCALL) && !cfg->generic_sharing_context && !vtable_arg && cmethod && (*ip == CEE_CALL) && - (mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod)))) { + (mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod))) && !MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->ret)) { MonoCallInst *call; /* Prevent inlining of methods with tail calls (the call stack would be altered) */ @@ -6115,7 +6318,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b call->method = cmethod; call->signature = mono_method_signature (cmethod); -#ifdef __x86_64__ +#ifdef TARGET_AMD64 /* Handle tail calls similarly to calls */ call->inst.opcode = OP_TAILCALL; call->args = sp; @@ -6159,7 +6362,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* Inlining */ if ((cfg->opt & MONO_OPT_INLINE) && cmethod && - (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || (cmethod->flags & METHOD_ATTRIBUTE_FINAL)) && + (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) && mono_method_check_inlining (cfg, cmethod) && !g_list_find (dont_inline, cmethod)) { int costs; @@ -6231,7 +6434,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (context_used && !imt_arg && !array_rank && (!mono_method_is_generic_sharable_impl (cmethod, TRUE) || !mono_class_generic_sharing_enabled (cmethod->klass)) && - (!virtual || cmethod->flags & METHOD_ATTRIBUTE_FINAL || + (!virtual || MONO_METHOD_IS_FINAL (cmethod) || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) { INLINE_FAILURE; @@ -6257,6 +6460,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b else if (*ip == CEE_CALLI) g_assert (!vtable_arg); else + /* FIXME: what the hell is this??? */ g_assert (cmethod->flags & METHOD_ATTRIBUTE_FINAL || !(cmethod->flags & METHOD_ATTRIBUTE_FINAL)); @@ -6273,49 +6477,24 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b call = (MonoCallInst*)ins; mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE); cfg->uses_rgctx_reg = TRUE; + call->rgctx_reg = TRUE; #else NOT_IMPLEMENTED; #endif } else { - ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr); - } - if (!MONO_TYPE_IS_VOID (fsig->ret)) { - if (fsig->pinvoke && !fsig->ret->byref) { - int widen_op = -1; - + if (addr->opcode == OP_AOTCONST && addr->inst_c1 == MONO_PATCH_INFO_ICALL_ADDR) { /* - * Native code might return non register sized integers - * without initializing the upper bits. + * Instead of emitting an indirect call, emit a direct call + * with the contents of the aotconst as the patch info. */ - switch (mono_type_to_load_membase (cfg, fsig->ret)) { - case OP_LOADI1_MEMBASE: - widen_op = OP_ICONV_TO_I1; - break; - case OP_LOADU1_MEMBASE: - widen_op = OP_ICONV_TO_U1; - break; - case OP_LOADI2_MEMBASE: - widen_op = OP_ICONV_TO_I2; - break; - case OP_LOADU2_MEMBASE: - widen_op = OP_ICONV_TO_U2; - break; - default: - break; - } - - if (widen_op != -1) { - int dreg = alloc_preg (cfg); - MonoInst *widen; - - EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg); - widen->type = ins->type; - ins = widen; - } + ins = (MonoInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_ICALL_ADDR, addr->inst_p0, fsig, sp); + NULLIFY_INS (addr); + } else { + ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr); } - - *sp++ = ins; } + if (!MONO_TYPE_IS_VOID (fsig->ret)) + *sp++ = mono_emit_widen_call_res (cfg, ins, fsig); ip += 5; ins_flag = 0; @@ -6363,7 +6542,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins = mini_redirect_call (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL); if (ins) { if (!MONO_TYPE_IS_VOID (fsig->ret)) - *sp++ = ins; + *sp++ = mono_emit_widen_call_res (cfg, ins, fsig); ip += 5; ins_flag = 0; @@ -6382,7 +6561,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } if (!MONO_TYPE_IS_VOID (fsig->ret)) - *sp++ = ins; + *sp++ = mono_emit_widen_call_res (cfg, ins, fsig); ip += 5; ins_flag = 0; @@ -6547,7 +6726,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b type_from_op (cmp, sp [0], NULL); CHECK_TYPE (cmp); -#if SIZEOF_VOID_P == 4 +#if SIZEOF_REGISTER == 4 if (cmp->opcode == OP_LCOMPARE_IMM) { /* Convert it to OP_LCOMPARE */ MONO_INST_NEW (cfg, ins, OP_I8CONST); @@ -6655,13 +6834,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b table->table_size = n; use_op_switch = FALSE; -#ifdef __arm__ +#ifdef TARGET_ARM /* ARM implements SWITCH statements differently */ /* FIXME: Make it use the generic implementation */ if (!cfg->compile_aot) use_op_switch = TRUE; #endif - + + if (COMPILE_LLVM (cfg)) + use_op_switch = TRUE; + if (use_op_switch) { MONO_INST_NEW (cfg, ins, OP_SWITCH); ins->sreg1 = src1->dreg; @@ -6675,7 +6857,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b else MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 2); -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 /* The upper word might not be zero, and we add it to a 64 bit address later */ MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, offset_reg, offset_reg); #endif @@ -6744,6 +6926,17 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b CHECK_STACK (2); sp -= 2; +#if HAVE_WRITE_BARRIERS + if (*ip == CEE_STIND_REF && method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && !((sp [1]->opcode == OP_PCONST) && (sp [1]->inst_p0 == 0))) { + /* insert call to write barrier */ + MonoMethod *write_barrier = mono_gc_get_write_barrier (); + mono_emit_method_call (cfg, write_barrier, sp, NULL); + ins_flag = 0; + ip++; + break; + } +#endif + NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg); ins->flags |= ins_flag; ins_flag = 0; @@ -6776,9 +6969,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } MONO_ADD_INS ((cfg)->cbb, (ins)); - *sp++ = ins; - mono_decompose_opcode (cfg, ins); + *sp++ = mono_decompose_opcode (cfg, ins); ip++; break; case CEE_ADD: @@ -6814,7 +7006,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (imm_opcode != -1) { ins->opcode = imm_opcode; if (sp [1]->opcode == OP_I8CONST) { -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 ins->inst_imm = sp [1]->inst_l; #else ins->inst_ls_word = sp [1]->inst_ls_word; @@ -6831,9 +7023,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } MONO_ADD_INS ((cfg)->cbb, (ins)); - *sp++ = ins; - mono_decompose_opcode (cfg, ins); + *sp++ = mono_decompose_opcode (cfg, ins); ip++; break; case CEE_NEG: @@ -6856,7 +7047,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b int data = sp [-1]->inst_c0; sp [-1]->opcode = OP_I8CONST; sp [-1]->type = STACK_I8; -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 if ((*ip) == CEE_CONV_U8) sp [-1]->inst_c0 = (guint32)data; else @@ -7046,7 +7237,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (bblock->out_of_line) { MonoInst *iargs [2]; - if (cfg->method->klass->image == mono_defaults.corlib) { + if (image == mono_defaults.corlib) { /* * Avoid relocations in AOT and save some space by using a * version of helper_ldstr specialized to mscorlib. @@ -7138,7 +7329,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * Generate smaller code for the common newobj instruction in * argument checking code. */ - if (bblock->out_of_line && cmethod->klass->image == mono_defaults.corlib && n <= 2 && + if (bblock->out_of_line && cmethod->klass->image == mono_defaults.corlib && + is_exception_class (cmethod->klass) && n <= 2 && ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) && ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) { MonoInst *iargs [3]; @@ -7185,11 +7377,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b iargs [0] = NULL; if (mini_class_is_system_array (cmethod->klass)) { - if (context_used) - GENERIC_SHARING_FAILURE (*ip); - g_assert (!context_used); g_assert (!vtable_arg); - EMIT_NEW_METHODCONST (cfg, *sp, cmethod); + + if (context_used) { + *sp = emit_get_rgctx_method (cfg, context_used, + cmethod, MONO_RGCTX_INFO_METHOD); + } else { + EMIT_NEW_METHODCONST (cfg, *sp, cmethod); + } /* Avoid varargs in the common case */ if (fsig->param_count == 1) @@ -7281,16 +7476,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b !mono_class_generic_sharing_enabled (cmethod->klass))) { MonoInst *cmethod_addr; - g_assert (!callvirt_this_arg); - cmethod_addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE); mono_emit_rgctx_calli (cfg, fsig, sp, cmethod_addr, vtable_arg); } else { INLINE_FAILURE; - mono_emit_rgctx_method_call_full (cfg, cmethod, fsig, sp, - callvirt_this_arg, NULL, vtable_arg); + ins = mono_emit_rgctx_method_call_full (cfg, cmethod, fsig, sp, + callvirt_this_arg, NULL, vtable_arg); + if (mono_method_is_generic_sharable_impl (cmethod, TRUE) && ((MonoCallInst*)ins)->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) + GENERIC_SHARING_FAILURE (*ip); } } @@ -7633,6 +7828,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b FIELD_ACCESS_FAILURE; mono_class_init (klass); + /* XXX this is technically required but, so far (SL2), no [SecurityCritical] types (not many exists) have + any visible *instance* field (in fact there's a single case for a static field in Marshal) XXX + if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) + ensure_method_is_allowed_to_access_field (cfg, method, field, bblock, ip); + */ + foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset; if (*ip == CEE_STFLD) { if (target_type_is_incompatible (cfg, field->type, sp [1])) @@ -7663,6 +7864,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } else { MonoInst *store; +#if HAVE_WRITE_BARRIERS + if (mini_type_to_stind (cfg, field->type) == CEE_STIND_REF && !(sp [1]->opcode == OP_PCONST && sp [1]->inst_c0 == 0)) { + /* insert call to write barrier */ + MonoMethod *write_barrier = mono_gc_get_write_barrier (); + MonoInst *iargs [2]; + int dreg; + + dreg = alloc_preg (cfg); + EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dreg, sp [0]->dreg, foffset); + iargs [1] = sp [1]; + mono_emit_method_call (cfg, write_barrier, iargs, NULL); + } +#endif + EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg); store->flags |= ins_flag; @@ -7680,7 +7895,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b EMIT_NEW_CLASSCONST (cfg, iargs [1], klass); EMIT_NEW_FIELDCONST (cfg, iargs [2], field); EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset); - if ((cfg->opt & MONO_OPT_INLINE) && !MONO_TYPE_ISSTRUCT (mono_method_signature (wrapper)->ret)) { + if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) { costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), iargs, ip, cfg->real_offset, dont_inline, TRUE); bblock = cfg->cbb; @@ -7752,6 +7967,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field)) FIELD_ACCESS_FAILURE; + /* if the class is Critical then transparent code cannot access it's fields */ + if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) + ensure_method_is_allowed_to_access_field (cfg, method, field, bblock, ip); + /* * We can only support shared generic static * field access on architectures where the @@ -7919,7 +8138,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b gpointer addr = (char*)vtable->data + field->offset; int ro_type = field->type->type; if (ro_type == MONO_TYPE_VALUETYPE && field->type->data.klass->enumtype) { - ro_type = field->type->data.klass->enum_basetype->type; + ro_type = mono_class_enum_basetype (field->type->data.klass)->type; } /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/ is_const = TRUE; @@ -7951,6 +8170,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr)); sp++; break; +#ifndef HAVE_MOVING_COLLECTOR case MONO_TYPE_I: case MONO_TYPE_U: case MONO_TYPE_STRING: @@ -7964,6 +8184,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b type_to_eval_stack_type ((cfg), field->type, *sp); sp++; break; +#endif case MONO_TYPE_I8: case MONO_TYPE_U8: EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr)); @@ -8262,9 +8483,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->dreg = alloc_freg (cfg); ins->type = STACK_R8; MONO_ADD_INS (bblock, ins); - *sp++ = ins; - mono_decompose_opcode (cfg, ins); + *sp++ = mono_decompose_opcode (cfg, ins); ++ip; break; @@ -8380,7 +8600,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b goto load_error; mono_class_init (handle_class); if (cfg->generic_sharing_context) { - if (handle_class == mono_defaults.typehandle_class) { + if (mono_metadata_token_table (n) == MONO_TABLE_TYPEDEF || + mono_metadata_token_table (n) == MONO_TABLE_TYPEREF) { + /* This case handles ldtoken + of an open type, like for + typeof(Gen<>). */ + context_used = 0; + } else if (handle_class == mono_defaults.typehandle_class) { /* If we get a MONO_TYPE_CLASS then we need to provide the open type, not an @@ -8889,7 +9115,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cmp->sreg2 = sp [1]->dreg; type_from_op (cmp, sp [0], sp [1]); CHECK_TYPE (cmp); - if ((sp [0]->type == STACK_I8) || ((sizeof (gpointer) == 8) && ((sp [0]->type == STACK_PTR) || (sp [0]->type == STACK_OBJ) || (sp [0]->type == STACK_MP)))) + if ((sp [0]->type == STACK_I8) || ((SIZEOF_REGISTER == 8) && ((sp [0]->type == STACK_PTR) || (sp [0]->type == STACK_OBJ) || (sp [0]->type == STACK_MP)))) cmp->opcode = OP_LCOMPARE; else if (sp [0]->type == STACK_R8) cmp->opcode = OP_FCOMPARE; @@ -8916,7 +9142,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } case CEE_LDFTN: { MonoInst *argconst; - MonoMethod *cil_method, *ctor_method; + MonoMethod *cil_method; gboolean needs_static_rgctx_invoke; CHECK_STACK_OVF (1); @@ -8953,18 +9179,26 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* FIXME: SGEN support */ /* FIXME: handle shared static generic methods */ /* FIXME: handle this in shared code */ - if (!needs_static_rgctx_invoke && !context_used && (sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context)) && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) { - MonoInst *target_ins; + if (!needs_static_rgctx_invoke && !context_used && (sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ)) { + MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context); + if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) { + MonoInst *target_ins; + MonoMethod *invoke; - ip += 6; - if (cfg->verbose_level > 3) - g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL)); - target_ins = sp [-1]; - sp --; - *sp = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod); - ip += 5; - sp ++; - break; + invoke = mono_get_delegate_invoke (ctor_method->klass); + if (!invoke || !mono_method_signature (invoke)) + goto load_error; + + ip += 6; + if (cfg->verbose_level > 3) + g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL)); + target_ins = sp [-1]; + sp --; + *sp = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod); + ip += 5; + sp ++; + break; + } } #endif @@ -9112,7 +9346,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_ADD_INS (cfg->cbb, ins); cfg->flags |= MONO_CFG_HAS_ALLOCA; - if (header->init_locals) + if (init_locals) ins->flags |= MONO_INST_INIT; *sp++ = ins; @@ -9327,7 +9561,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cfg->method == method && cfg->got_var) mono_emit_load_got_addr (cfg); - if (header->init_locals) { + if (init_locals) { MonoInst *store; cfg->cbb = init_localsbb; @@ -9338,7 +9572,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b dreg = cfg->locals [i]->dreg; if (t == MONO_TYPE_VALUETYPE && ptype->data.klass->enumtype) - t = ptype->data.klass->enum_basetype->type; + t = mono_class_enum_basetype (ptype->data.klass)->type; if (ptype->byref) { MONO_EMIT_NEW_PCONST (cfg, dreg, NULL); } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) { @@ -9504,13 +9738,13 @@ mono_op_to_op_imm (int opcode) case OP_STOREI4_MEMBASE_REG: return OP_STOREI4_MEMBASE_IMM; -#if defined(__i386__) || defined (__x86_64__) +#if defined(TARGET_X86) || defined (TARGET_AMD64) case OP_X86_PUSH: return OP_X86_PUSH_IMM; case OP_X86_COMPARE_MEMBASE_REG: return OP_X86_COMPARE_MEMBASE_IMM; #endif -#if defined(__x86_64__) +#if defined(TARGET_AMD64) case OP_AMD64_ICOMPARE_MEMBASE_REG: return OP_AMD64_ICOMPARE_MEMBASE_IMM; #endif @@ -9592,7 +9826,7 @@ int mono_load_membase_to_load_mem (int opcode) { // FIXME: Add a MONO_ARCH_HAVE_LOAD_MEM macro -#if defined(__i386__) || defined(__x86_64__) +#if defined(TARGET_X86) || defined(TARGET_AMD64) switch (opcode) { case OP_LOAD_MEMBASE: return OP_LOAD_MEM; @@ -9604,7 +9838,7 @@ mono_load_membase_to_load_mem (int opcode) return OP_LOADI4_MEM; case OP_LOADU4_MEMBASE: return OP_LOADU4_MEM; -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 case OP_LOADI8_MEMBASE: return OP_LOADI8_MEM; #endif @@ -9617,7 +9851,7 @@ mono_load_membase_to_load_mem (int opcode) static inline int op_to_op_dest_membase (int store_opcode, int opcode) { -#if defined(__i386__) +#if defined(TARGET_X86) if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG))) return -1; @@ -9652,7 +9886,7 @@ op_to_op_dest_membase (int store_opcode, int opcode) } #endif -#if defined(__x86_64__) +#if defined(TARGET_AMD64) if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG) || (store_opcode == OP_STOREI8_MEMBASE_REG))) return -1; @@ -9713,7 +9947,7 @@ op_to_op_dest_membase (int store_opcode, int opcode) static inline int op_to_op_store_membase (int store_opcode, int opcode) { -#if defined(__i386__) || defined(__x86_64__) +#if defined(TARGET_X86) || defined(TARGET_AMD64) switch (opcode) { case OP_ICEQ: if (store_opcode == OP_STOREI1_MEMBASE_REG) @@ -9730,7 +9964,7 @@ op_to_op_store_membase (int store_opcode, int opcode) static inline int op_to_op_src1_membase (int load_opcode, int opcode) { -#ifdef __i386__ +#ifdef TARGET_X86 /* FIXME: This has sign extension issues */ /* if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE)) @@ -9752,7 +9986,7 @@ op_to_op_src1_membase (int load_opcode, int opcode) } #endif -#ifdef __x86_64__ +#ifdef TARGET_AMD64 /* FIXME: This has sign extension issues */ /* if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE)) @@ -9792,7 +10026,7 @@ op_to_op_src1_membase (int load_opcode, int opcode) static inline int op_to_op_src2_membase (int load_opcode, int opcode) { -#ifdef __i386__ +#ifdef TARGET_X86 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))) return -1; @@ -9813,7 +10047,7 @@ op_to_op_src2_membase (int load_opcode, int opcode) } #endif -#ifdef __x86_64__ +#ifdef TARGET_AMD64 switch (opcode) { case OP_ICOMPARE: if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)) @@ -9864,7 +10098,7 @@ int mono_op_to_op_imm_noemul (int opcode) { switch (opcode) { -#if SIZEOF_VOID_P == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS) +#if SIZEOF_REGISTER == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS) case OP_LSHR: case OP_LSHL: case OP_LSHR_UN: @@ -9922,7 +10156,7 @@ mono_handle_global_vregs (MonoCompile *cfg) g_assert (ins->opcode >= MONO_CEE_LAST); - for (regindex = 0; regindex < 3; regindex ++) { + for (regindex = 0; regindex < 4; regindex ++) { int vreg; if (regindex == 0) { @@ -9935,15 +10169,21 @@ mono_handle_global_vregs (MonoCompile *cfg) if (regtype == ' ') continue; vreg = ins->sreg1; - } else { + } else if (regindex == 2) { regtype = spec [MONO_INST_SRC2]; if (regtype == ' ') continue; vreg = ins->sreg2; + } else if (regindex == 3) { + regtype = spec [MONO_INST_SRC3]; + if (regtype == ' ') + continue; + vreg = ins->sreg3; } -#if SIZEOF_VOID_P == 4 - if (regtype == 'l') { +#if SIZEOF_REGISTER == 4 + /* In the LLVM case, the long opcodes are not decomposed */ + if (regtype == 'l' && !COMPILE_LLVM (cfg)) { /* * Since some instructions reference the original long vreg, * and some reference the two component vregs, it is quite hard @@ -10012,10 +10252,10 @@ mono_handle_global_vregs (MonoCompile *cfg) case STACK_PTR: case STACK_MP: case STACK_VTYPE: -#if SIZEOF_VOID_P == 8 +#if SIZEOF_REGISTER == 8 case STACK_I8: #endif -#if !defined(__i386__) && !defined(MONO_ARCH_SOFT_FLOAT) +#if !defined(TARGET_X86) && !defined(MONO_ARCH_SOFT_FLOAT) /* Enabling this screws up the fp stack on x86 */ case STACK_R8: #endif @@ -10086,7 +10326,7 @@ mono_handle_global_vregs (MonoCompile *cfg) cfg->varinfo [pos]->inst_c0 = pos; memcpy (&cfg->vars [pos], &cfg->vars [i], sizeof (MonoMethodVar)); cfg->vars [pos].idx = pos; -#if SIZEOF_VOID_P == 4 +#if SIZEOF_REGISTER == 4 if (cfg->varinfo [pos]->type == STACK_I8) { /* Modify the two component vars too */ MonoInst *var1; @@ -10124,6 +10364,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) guint32 i, lvregs_len; gboolean dest_has_lvreg = FALSE; guint32 stacktypes [128]; + MonoInst **live_range_start, **live_range_end; + MonoBasicBlock **live_range_start_bb, **live_range_end_bb; *need_local_opts = FALSE; @@ -10137,7 +10379,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) stacktypes ['x'] = STACK_VTYPE; #endif -#if SIZEOF_VOID_P == 4 +#if SIZEOF_REGISTER == 4 /* Create MonoInsts for longs */ for (i = 0; i < cfg->num_varinfo; i++) { MonoInst *ins = cfg->varinfo [i]; @@ -10183,6 +10425,20 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) vreg_to_lvreg = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg); lvregs = mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024); lvregs_len = 0; + + /* + * These arrays contain the first and last instructions accessing a given + * variable. + * Since we emit bblocks in the same order we process them here, and we + * don't split live ranges, these will precisely describe the live range of + * the variable, i.e. the instruction range where a valid value can be found + * in the variables location. + */ + /* FIXME: Only do this if debugging info is requested */ + live_range_start = g_new0 (MonoInst*, cfg->next_vreg); + live_range_end = g_new0 (MonoInst*, cfg->next_vreg); + live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg); + live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg); /* Add spill loads/stores */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { @@ -10199,8 +10455,9 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) cfg->cbb = bb; MONO_BB_FOR_EACH_INS (bb, ins) { const char *spec = INS_INFO (ins->opcode); - int regtype, srcindex, sreg, tmp_reg, prev_dreg; + int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs; gboolean store, no_lvreg; + int sregs [MONO_MAX_SRC_REGS]; if (G_UNLIKELY (cfg->verbose_level > 2)) mono_print_ins (ins); @@ -10259,6 +10516,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) spec2 [MONO_INST_DEST] = ' '; spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1]; spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST]; + spec2 [MONO_INST_SRC3] = ' '; spec = spec2; } else if (MONO_IS_STORE_MEMINDEX (ins)) g_assert_not_reached (); @@ -10266,8 +10524,13 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) store = FALSE; no_lvreg = FALSE; - if (G_UNLIKELY (cfg->verbose_level > 2)) - printf ("\t %.3s %d %d %d\n", spec, ins->dreg, ins->sreg1, ins->sreg2); + if (G_UNLIKELY (cfg->verbose_level > 2)) { + printf ("\t %.3s %d", spec, ins->dreg); + num_sregs = mono_inst_get_src_registers (ins, sregs); + for (srcindex = 0; srcindex < 3; ++srcindex) + printf (" %d", sregs [srcindex]); + printf ("\n"); + } /***************/ /* DREG */ @@ -10280,6 +10543,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) MonoInst *var = get_vreg_to_inst (cfg, ins->dreg); MonoInst *store_ins; int store_opcode; + MonoInst *def_ins = ins; + int dreg = ins->dreg; /* The original vreg */ store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype); @@ -10292,6 +10557,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) g_assert (var->opcode == OP_REGOFFSET); if (ins->opcode == OP_MOVE) { NULLIFY_INS (ins); + def_ins = NULL; } else { ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode); ins->inst_basereg = var->inst_basereg; @@ -10325,6 +10591,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) mono_bblock_insert_after_ins (bb, ins, store_ins); NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET, ins->dreg + 2); mono_bblock_insert_after_ins (bb, ins, store_ins); + def_ins = store_ins; } else { g_assert (store_opcode != OP_STOREV_MEMBASE); @@ -10336,6 +10603,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) ins->inst_imm = ins->inst_c0; ins->inst_destbasereg = var->inst_basereg; ins->inst_offset = var->inst_offset; + spec = INS_INFO (ins->opcode); } else if (!lvreg && ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE) || (ins->opcode == OP_LMOVE))) { ins->opcode = store_opcode; ins->inst_destbasereg = var->inst_basereg; @@ -10351,6 +10619,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) spec2 [MONO_INST_DEST] = ' '; spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1]; spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST]; + spec2 [MONO_INST_SRC3] = ' '; spec = spec2; } else if (!lvreg && (op_to_op_store_membase (store_opcode, ins->opcode) != -1)) { // FIXME: The backends expect the base reg to be in inst_basereg @@ -10367,6 +10636,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) /* Insert it after the instruction */ mono_bblock_insert_after_ins (bb, ins, store_ins); + def_ins = store_ins; + /* * We can't assign ins->dreg to var->dreg here, since the * sregs could use it. So set a flag, and do it after @@ -10377,26 +10648,33 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) } } } + + if (def_ins && !live_range_start [dreg]) { + live_range_start [dreg] = def_ins; + live_range_start_bb [dreg] = bb; + } } /************/ /* SREGS */ /************/ - for (srcindex = 0; srcindex < 2; ++srcindex) { - regtype = spec [(srcindex == 0) ? MONO_INST_SRC1 : MONO_INST_SRC2]; - sreg = srcindex == 0 ? ins->sreg1 : ins->sreg2; + num_sregs = mono_inst_get_src_registers (ins, sregs); + for (srcindex = 0; srcindex < 3; ++srcindex) { + regtype = spec [MONO_INST_SRC1 + srcindex]; + sreg = sregs [srcindex]; g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' '))); if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) { MonoInst *var = get_vreg_to_inst (cfg, sreg); + MonoInst *use_ins = ins; MonoInst *load_ins; guint32 load_opcode; if (var->opcode == OP_REGVAR) { - if (srcindex == 0) - ins->sreg1 = var->dreg; - else - ins->sreg2 = var->dreg; + sregs [srcindex] = var->dreg; + //mono_inst_set_src_registers (ins, sregs); + live_range_end [sreg] = use_ins; + live_range_end_bb [sreg] = bb; continue; } @@ -10407,24 +10685,26 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) g_assert (load_opcode != OP_LOADV_MEMBASE); if (vreg_to_lvreg [sreg]) { + g_assert (vreg_to_lvreg [sreg] != -1); + /* The variable is already loaded to an lvreg */ if (G_UNLIKELY (cfg->verbose_level > 2)) printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg); - if (srcindex == 0) - ins->sreg1 = vreg_to_lvreg [sreg]; - else - ins->sreg2 = vreg_to_lvreg [sreg]; + sregs [srcindex] = vreg_to_lvreg [sreg]; + //mono_inst_set_src_registers (ins, sregs); continue; } /* Try to fuse the load into the instruction */ if ((srcindex == 0) && (op_to_op_src1_membase (load_opcode, ins->opcode) != -1)) { ins->opcode = op_to_op_src1_membase (load_opcode, ins->opcode); - ins->inst_basereg = var->inst_basereg; + sregs [0] = var->inst_basereg; + //mono_inst_set_src_registers (ins, sregs); ins->inst_offset = var->inst_offset; } else if ((srcindex == 1) && (op_to_op_src2_membase (load_opcode, ins->opcode) != -1)) { ins->opcode = op_to_op_src2_membase (load_opcode, ins->opcode); - ins->sreg2 = var->inst_basereg; + sregs [1] = var->inst_basereg; + //mono_inst_set_src_registers (ins, sregs); ins->inst_offset = var->inst_offset; } else { if (MONO_IS_REAL_MOVE (ins)) { @@ -10444,35 +10724,43 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) */ sreg = ins->dreg; } + g_assert (sreg != -1); vreg_to_lvreg [var->dreg] = sreg; g_assert (lvregs_len < 1024); lvregs [lvregs_len ++] = var->dreg; } } - if (srcindex == 0) - ins->sreg1 = sreg; - else - ins->sreg2 = sreg; + sregs [srcindex] = sreg; + //mono_inst_set_src_registers (ins, sregs); if (regtype == 'l') { NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 2, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET); mono_bblock_insert_before_ins (bb, ins, load_ins); NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 1, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET); mono_bblock_insert_before_ins (bb, ins, load_ins); + use_ins = load_ins; } else { -#if SIZEOF_VOID_P == 4 +#if SIZEOF_REGISTER == 4 g_assert (load_opcode != OP_LOADI8_MEMBASE); #endif NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset); mono_bblock_insert_before_ins (bb, ins, load_ins); + use_ins = load_ins; } } + + if (var->dreg < orig_next_vreg) { + live_range_end [var->dreg] = use_ins; + live_range_end_bb [var->dreg] = bb; + } } } + mono_inst_set_src_registers (ins, sregs); if (dest_has_lvreg) { + g_assert (ins->dreg != -1); vreg_to_lvreg [prev_dreg] = ins->dreg; g_assert (lvregs_len < 1024); lvregs [lvregs_len ++] = prev_dreg; @@ -10490,12 +10778,44 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) for (i = 0; i < lvregs_len; i++) vreg_to_lvreg [lvregs [i]] = 0; lvregs_len = 0; + } else if (ins->opcode == OP_NOP) { + ins->dreg = -1; + MONO_INST_NULLIFY_SREGS (ins); } if (cfg->verbose_level > 2) mono_print_ins_index (1, ins); } } + +#ifdef MONO_ARCH_HAVE_LIVERANGE_OPS + /* + * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them + * by storing the current native offset into MonoMethodVar->live_range_start/end. + */ + for (i = 0; i < cfg->num_varinfo; ++i) { + int vreg = MONO_VARINFO (cfg, i)->vreg; + MonoInst *ins; + + if (live_range_start [vreg]) { + MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START); + ins->inst_c0 = i; + ins->inst_c1 = vreg; + mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins); + } + if (live_range_end [vreg]) { + MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END); + ins->inst_c0 = i; + ins->inst_c1 = vreg; + mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins); + } + } +#endif + + g_free (live_range_start); + g_free (live_range_end); + g_free (live_range_start_bb); + g_free (live_range_end_bb); } /** @@ -10573,12 +10893,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) * fcompare + branchCC. * - create a helper function for allocating a stack slot, taking into account * MONO_CFG_HAS_SPILLUP. - * - merge new GC changes in mini.c. * - merge r68207. * - merge the ia64 switch changes. - * - merge the mips conditional changes. - * - remove unused opcodes from mini-ops.h, remove "op_" from the opcode names, - * - make the cpu_ tables smaller when the usage of the cee_ opcodes is removed. * - optimize mono_regstate2_alloc_int/float. * - fix the pessimistic handling of variables accessed in exception handler blocks. * - need to write a tree optimization pass, but the creation of trees is difficult, i.e.