X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmethod-to-ir.c;h=18e1483945b212798d7baa1bacebd29b398c12f1;hb=9ab38c6192912cc1efb8178d8cd5fe00d7c90a78;hp=c54af2302be9d13d9ab7ab6d404e9c43f940e813;hpb=02f2bed7339f9c6718ea93817413c9581299eb41;p=mono.git diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index c54af2302be..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,7 +121,11 @@ 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' @@ -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) { @@ -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)); \ @@ -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; @@ -1607,19 +1629,19 @@ 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; + 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); @@ -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; @@ -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; @@ -2344,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 @@ -2407,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) @@ -2771,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)); @@ -3257,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); } @@ -3301,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); @@ -3318,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) || @@ -3438,12 +3520,17 @@ mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, Mono 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 }; @@ -3576,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 */ @@ -3860,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; @@ -4051,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); @@ -4104,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; @@ -4118,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) @@ -4158,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) { @@ -4440,53 +4541,83 @@ 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 = verification_exception (); + MonoMethod *thrower = field_access_exception (); + MonoInst *args [2]; - mono_emit_method_call (cfg, thrower, NULL, NULL); + 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_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee, - MonoBasicBlock *bblock, unsigned char *ip) +ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field, + 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 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; - 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) - emit_throw_method_access_exception (cfg, caller, callee, bblock, ip); + /* 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 gboolean -method_is_safe (MonoMethod *method) +static void +ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee, + MonoBasicBlock *bblock, unsigned char *ip) { - /* - if (strcmp (method->name, "unsafeMethod") == 0) - return FALSE; - */ - return 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; + + /* 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; + + /* 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); } /* @@ -4943,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); @@ -4979,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 @@ -5033,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: * @@ -5068,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; @@ -5093,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) { @@ -5263,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); @@ -5325,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; @@ -5386,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) @@ -5407,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; @@ -5483,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; @@ -5672,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); @@ -5692,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); @@ -5734,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); @@ -5743,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; @@ -5756,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; @@ -5833,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; @@ -5888,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 (); } @@ -6058,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 @@ -6103,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; @@ -6114,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) */ @@ -6125,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; @@ -6284,6 +6477,7 @@ 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 @@ -6299,43 +6493,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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; - - /* - * 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; - } - } - - *sp++ = ins; - } + if (!MONO_TYPE_IS_VOID (fsig->ret)) + *sp++ = mono_emit_widen_call_res (cfg, ins, fsig); ip += 5; ins_flag = 0; @@ -6383,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; @@ -6402,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; @@ -6675,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; @@ -6807,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: @@ -6862,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: @@ -7169,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]; @@ -7216,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) @@ -7318,8 +7482,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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); } } @@ -7662,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])) @@ -7723,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; @@ -7795,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 @@ -7962,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; @@ -8307,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; @@ -9008,6 +9183,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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; + + invoke = mono_get_delegate_invoke (ctor_method->klass); + if (!invoke || !mono_method_signature (invoke)) + goto load_error; ip += 6; if (cfg->verbose_level > 3) @@ -9166,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; @@ -9381,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; @@ -9392,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) { @@ -9558,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 @@ -9646,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; @@ -9671,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; @@ -9706,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; @@ -9767,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) @@ -9784,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)) @@ -9806,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)) @@ -9846,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; @@ -9867,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)) @@ -9976,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) { @@ -9989,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_REGISTER == 4 - if (regtype == 'l') { + /* 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 @@ -10069,7 +10255,7 @@ mono_handle_global_vregs (MonoCompile *cfg) #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 @@ -10178,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; @@ -10237,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) { @@ -10253,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); @@ -10313,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 (); @@ -10320,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 */ @@ -10334,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); @@ -10346,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; @@ -10379,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); @@ -10390,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; @@ -10405,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 @@ -10421,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 @@ -10431,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; } @@ -10461,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)) { @@ -10498,22 +10724,22 @@ 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_REGISTER == 4 @@ -10521,12 +10747,20 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) #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; @@ -10544,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); } /**