X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini-x86.c;h=6da3071a44ad6c3c36387fd680bf120f9101a832;hb=00d7c8f8ff851982c2df4d1eb3836bd57a6bad22;hp=5960329ecec8dd132d6432097dc1e20a31cb3a33;hpb=9869ae24b88761ab261c4311e24f7383b4af3f02;p=mono.git diff --git a/mono/mini/mini-x86.c b/mono/mini/mini-x86.c index 5960329ecec..6da3071a44a 100644 --- a/mono/mini/mini-x86.c +++ b/mono/mini/mini-x86.c @@ -11,7 +11,9 @@ #include "mini.h" #include #include +#ifdef HAVE_UNISTD_H #include +#endif #include #include @@ -26,16 +28,22 @@ /* On windows, these hold the key returned by TlsAlloc () */ static gint lmf_tls_offset = -1; +static gint lmf_addr_tls_offset = -1; static gint appdomain_tls_offset = -1; static gint thread_tls_offset = -1; #ifdef MONO_XEN_OPT -/* TRUE by default until we add runtime detection of Xen */ static gboolean optimize_for_xen = TRUE; #else #define optimize_for_xen 0 #endif +#ifdef PLATFORM_WIN32 +static gboolean is_win32 = TRUE; +#else +static gboolean is_win32 = FALSE; +#endif + #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) #define ARGS_OFFSET 8 @@ -107,7 +115,8 @@ typedef struct { static X86_Reg_No param_regs [] = { 0 }; -#ifdef PLATFORM_WIN32 +#if defined(PLATFORM_WIN32) || defined(__APPLE__) || defined(__FreeBSD__) +#define SMALL_STRUCTS_IN_REGS static X86_Reg_No return_regs [] = { X86_EAX, X86_EDX }; #endif @@ -173,7 +182,7 @@ add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, else size = mono_type_stack_size (&klass->byval_arg, NULL); -#ifdef PLATFORM_WIN32 +#ifdef SMALL_STRUCTS_IN_REGS if (sig->pinvoke && is_return) { MonoMarshalType *info; @@ -225,7 +234,7 @@ add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, * For x86 win32, see ???. */ static CallInfo* -get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) +get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) { guint32 i, gr, fr; MonoType *ret_type; @@ -233,7 +242,10 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) guint32 stack_size = 0; CallInfo *cinfo; - cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n)); + if (mp) + cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n)); + else + cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n)); gr = 0; fr = 0; @@ -432,7 +444,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit int offset = 8; CallInfo *cinfo; - cinfo = get_call_info (csig, FALSE); + cinfo = get_call_info (NULL, csig, FALSE); if (MONO_TYPE_ISSTRUCT (csig->ret) && (cinfo->ret.storage == ArgOnStack)) { frame_size += sizeof (gpointer); @@ -541,16 +553,15 @@ cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx) #endif if (have_cpuid) { /* Have to use the code manager to get around WinXP DEP */ - MonoCodeManager *codeman = mono_code_manager_new_dynamic (); - CpuidFunc func; - void *ptr = mono_code_manager_reserve (codeman, sizeof (cpuid_impl)); - memcpy (ptr, cpuid_impl, sizeof (cpuid_impl)); - - func = (CpuidFunc)ptr; + static CpuidFunc func = NULL; + void *ptr; + if (!func) { + ptr = mono_global_codeman_reserve (sizeof (cpuid_impl)); + memcpy (ptr, cpuid_impl, sizeof (cpuid_impl)); + func = (CpuidFunc)ptr; + } func (id, p_eax, p_ebx, p_ecx, p_edx); - mono_code_manager_destroy (codeman); - /* * We use this approach because of issues with gcc and pic code, see: * http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=7329 @@ -656,34 +667,6 @@ mono_arch_is_int_overflow (void *sigctx, void *info) return FALSE; } -static gboolean -is_regsize_var (MonoType *t) { - if (t->byref) - return TRUE; - switch (mono_type_get_underlying_type (t)->type) { - case MONO_TYPE_I4: - case MONO_TYPE_U4: - case MONO_TYPE_I: - case MONO_TYPE_U: - case MONO_TYPE_PTR: - case MONO_TYPE_FNPTR: - return TRUE; - case MONO_TYPE_OBJECT: - case MONO_TYPE_STRING: - case MONO_TYPE_CLASS: - case MONO_TYPE_SZARRAY: - case MONO_TYPE_ARRAY: - return TRUE; - case MONO_TYPE_GENERICINST: - if (!mono_type_generic_inst_is_valuetype (t)) - return TRUE; - return FALSE; - case MONO_TYPE_VALUETYPE: - return FALSE; - } - return FALSE; -} - GList * mono_arch_get_allocatable_int_vars (MonoCompile *cfg) { @@ -704,9 +687,7 @@ mono_arch_get_allocatable_int_vars (MonoCompile *cfg) /* we dont allocate I1 to registers because there is no simply way to sign extend * 8bit quantities in caller saved registers on x86 */ - if (is_regsize_var (ins->inst_vtype) || (ins->inst_vtype->type == MONO_TYPE_BOOLEAN) || - (ins->inst_vtype->type == MONO_TYPE_U1) || (ins->inst_vtype->type == MONO_TYPE_U2)|| - (ins->inst_vtype->type == MONO_TYPE_I2) || (ins->inst_vtype->type == MONO_TYPE_CHAR)) { + if (mono_is_regsize_var (ins->inst_vtype) && (ins->inst_vtype->type != MONO_TYPE_I1)) { g_assert (MONO_VARINFO (cfg, i)->reg == -1); g_assert (i == vmv->idx); vars = g_list_prepend (vars, vmv); @@ -769,7 +750,7 @@ mono_arch_allocate_vars (MonoCompile *cfg) header = mono_method_get_header (cfg->method); sig = mono_method_signature (cfg->method); - cinfo = get_call_info (sig, FALSE); + cinfo = get_call_info (cfg->mempool, sig, FALSE); cfg->frame_reg = MONO_ARCH_BASEREG; offset = 0; @@ -819,7 +800,6 @@ mono_arch_allocate_vars (MonoCompile *cfg) //printf ("allocated local %d to ", i); mono_print_tree_nl (inst); } } - g_free (offsets); offset += locals_stack_size; @@ -854,7 +834,7 @@ mono_arch_allocate_vars (MonoCompile *cfg) for (i = 0; i < sig->param_count + sig->hasthis; ++i) { ArgInfo *ainfo = &cinfo->args [i]; - inst = cfg->varinfo [i]; + inst = cfg->args [i]; if (inst->opcode != OP_REGVAR) { inst->opcode = OP_REGOFFSET; inst->inst_basereg = X86_EBP; @@ -866,8 +846,6 @@ mono_arch_allocate_vars (MonoCompile *cfg) offset &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1); cfg->stack_offset = offset; - - g_free (cinfo); } void @@ -878,12 +856,10 @@ mono_arch_create_vars (MonoCompile *cfg) sig = mono_method_signature (cfg->method); - cinfo = get_call_info (sig, FALSE); + cinfo = get_call_info (cfg->mempool, sig, FALSE); if (cinfo->ret.storage == ArgValuetypeInReg) cfg->ret_var_is_local = TRUE; - - g_free (cinfo); } /* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode, @@ -939,7 +915,7 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, sig = call->signature; n = sig->param_count + sig->hasthis; - cinfo = get_call_info (sig, FALSE); + cinfo = get_call_info (cfg->mempool, sig, FALSE); if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG)) sentinelpos = sig->sentinelpos + (is_virtual ? 1 : 0); @@ -1053,8 +1029,6 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, } #endif - g_free (cinfo); - return call; } @@ -1066,6 +1040,10 @@ mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean ena { guchar *code = p; +#if __APPLE__ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); +#endif + /* if some args are passed in registers, we need to save them here */ x86_push_reg (code, X86_EBP); @@ -1079,7 +1057,11 @@ mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean ena mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func); x86_call_code (code, 0); } +#if __APPLE__ + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 16); +#else x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8); +#endif return code; } @@ -1254,40 +1236,44 @@ emit_call (MonoCompile *cfg, guint8 *code, guint32 patch_type, gconstpointer dat return code; } -/* FIXME: Add more instructions */ -#define INST_IGNORES_CFLAGS(ins) (((ins)->opcode == CEE_BR) || ((ins)->opcode == OP_STORE_MEMBASE_IMM) || ((ins)->opcode == OP_STOREI4_MEMBASE_REG)) +#define INST_IGNORES_CFLAGS(opcode) (!(((opcode) == OP_ADC) || ((opcode) == OP_IADC) || ((opcode) == OP_ADC_IMM) || ((opcode) == OP_IADC_IMM) || ((opcode) == OP_SBB) || ((opcode) == OP_ISBB) || ((opcode) == OP_SBB_IMM) || ((opcode) == OP_ISBB_IMM))) +/* + * peephole_pass_1: + * + * Perform peephole opts which should/can be performed before local regalloc + */ static void -peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb) +peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb) { MonoInst *ins, *last_ins = NULL; ins = bb->code; while (ins) { - switch (ins->opcode) { - case OP_ICONST: - /* reg = 0 -> XOR (reg, reg) */ - /* XOR sets cflags on x86, so we cant do it always */ - if (ins->inst_c0 == 0 && ins->next && INST_IGNORES_CFLAGS (ins->next)) { - ins->opcode = CEE_XOR; - ins->sreg1 = ins->dreg; - ins->sreg2 = ins->dreg; - } + case OP_IADD_IMM: + case OP_ADD_IMM: + if ((ins->sreg1 < MONO_MAX_IREGS) && (ins->dreg >= MONO_MAX_IREGS)) { + /* + * X86_LEA is like ADD, but doesn't have the + * sreg1==dreg restriction. + */ + ins->opcode = OP_X86_LEA_MEMBASE; + ins->inst_basereg = ins->sreg1; + } else if ((ins->inst_imm == 1) && (ins->dreg == ins->sreg1)) + ins->opcode = OP_X86_INC_REG; break; - case OP_MUL_IMM: - /* remove unnecessary multiplication with 1 */ - if (ins->inst_imm == 1) { - if (ins->dreg != ins->sreg1) { - ins->opcode = OP_MOVE; - } else { - last_ins->next = ins->next; - ins = ins->next; - continue; - } - } + case OP_SUB_IMM: + case OP_ISUB_IMM: + if ((ins->sreg1 < MONO_MAX_IREGS) && (ins->dreg >= MONO_MAX_IREGS)) { + ins->opcode = OP_X86_LEA_MEMBASE; + ins->inst_basereg = ins->sreg1; + ins->inst_imm = -ins->inst_imm; + } else if ((ins->inst_imm == 1) && (ins->dreg == ins->sreg1)) + ins->opcode = OP_X86_DEC_REG; break; case OP_COMPARE_IMM: + case OP_ICOMPARE_IMM: /* OP_COMPARE_IMM (reg, 0) * --> * OP_X86_TEST_NULL (reg) @@ -1423,6 +1409,7 @@ peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb) break; case CEE_CONV_I4: case CEE_CONV_U4: + case OP_ICONV_TO_I4: case OP_MOVE: /* * Removes: @@ -1466,6 +1453,227 @@ peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb) bb->last_ins = last_ins; } +static void +peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoInst *ins, *last_ins = NULL; + ins = bb->code; + + while (ins) { + + switch (ins->opcode) { + case OP_ICONST: + /* reg = 0 -> XOR (reg, reg) */ + /* XOR sets cflags on x86, so we cant do it always */ + if (ins->inst_c0 == 0 && (!ins->next || (ins->next && INST_IGNORES_CFLAGS (ins->next->opcode)))) { + MonoInst *ins2; + + ins->opcode = OP_IXOR; + ins->sreg1 = ins->dreg; + ins->sreg2 = ins->dreg; + + /* + * Convert succeeding STORE_MEMBASE_IMM 0 ins to STORE_MEMBASE_REG + * since it takes 3 bytes instead of 7. + */ + for (ins2 = ins->next; ins2; ins2 = ins2->next) { + if ((ins2->opcode == OP_STORE_MEMBASE_IMM) && (ins2->inst_imm == 0)) { + ins2->opcode = OP_STORE_MEMBASE_REG; + ins2->sreg1 = ins->dreg; + } + else if ((ins2->opcode == OP_STOREI4_MEMBASE_IMM) && (ins2->inst_imm == 0)) { + ins2->opcode = OP_STOREI4_MEMBASE_REG; + ins2->sreg1 = ins->dreg; + } + else if ((ins2->opcode == OP_STOREI1_MEMBASE_IMM) || (ins2->opcode == OP_STOREI2_MEMBASE_IMM)) { + /* Continue iteration */ + } + else + break; + } + } + break; + case OP_IADD_IMM: + case OP_ADD_IMM: + if ((ins->inst_imm == 1) && (ins->dreg == ins->sreg1)) + ins->opcode = OP_X86_INC_REG; + break; + case OP_ISUB_IMM: + case OP_SUB_IMM: + if ((ins->inst_imm == 1) && (ins->dreg == ins->sreg1)) + ins->opcode = OP_X86_DEC_REG; + break; + case OP_X86_COMPARE_MEMBASE_IMM: + /* + * OP_STORE_MEMBASE_REG reg, offset(basereg) + * OP_X86_COMPARE_MEMBASE_IMM offset(basereg), imm + * --> + * OP_STORE_MEMBASE_REG reg, offset(basereg) + * OP_COMPARE_IMM reg, imm + * + * Note: if imm = 0 then OP_COMPARE_IMM replaced with OP_X86_TEST_NULL + */ + if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + ins->opcode = OP_COMPARE_IMM; + ins->sreg1 = last_ins->sreg1; + + /* check if we can remove cmp reg,0 with test null */ + if (!ins->inst_imm) + ins->opcode = OP_X86_TEST_NULL; + } + + break; + case OP_LOAD_MEMBASE: + case OP_LOADI4_MEMBASE: + /* + * Note: if reg1 = reg2 the load op is removed + * + * OP_STORE_MEMBASE_REG reg1, offset(basereg) + * OP_LOAD_MEMBASE offset(basereg), reg2 + * --> + * OP_STORE_MEMBASE_REG reg1, offset(basereg) + * OP_MOVE reg1, reg2 + */ + if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG + || last_ins->opcode == OP_STORE_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + if (ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->sreg1; + } + + /* + * Note: reg1 must be different from the basereg in the second load + * Note: if reg1 = reg2 is equal then second load is removed + * + * OP_LOAD_MEMBASE offset(basereg), reg1 + * OP_LOAD_MEMBASE offset(basereg), reg2 + * --> + * OP_LOAD_MEMBASE offset(basereg), reg1 + * OP_MOVE reg1, reg2 + */ + } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE + || last_ins->opcode == OP_LOAD_MEMBASE) && + ins->inst_basereg != last_ins->dreg && + ins->inst_basereg == last_ins->inst_basereg && + ins->inst_offset == last_ins->inst_offset) { + + if (ins->dreg == last_ins->dreg) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } else { + ins->opcode = OP_MOVE; + ins->sreg1 = last_ins->dreg; + } + + //g_assert_not_reached (); + +#if 0 + /* + * OP_STORE_MEMBASE_IMM imm, offset(basereg) + * OP_LOAD_MEMBASE offset(basereg), reg + * --> + * OP_STORE_MEMBASE_IMM imm, offset(basereg) + * OP_ICONST reg, imm + */ + } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM + || last_ins->opcode == OP_STORE_MEMBASE_IMM) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++); + ins->opcode = OP_ICONST; + ins->inst_c0 = last_ins->inst_imm; + g_assert_not_reached (); // check this rule +#endif + } + break; + case OP_LOADU1_MEMBASE: + case OP_LOADI1_MEMBASE: + /* + * OP_STORE_MEMBASE_REG reg1, offset(basereg) + * OP_LOAD_MEMBASE offset(basereg), reg2 + * --> + * OP_STORE_MEMBASE_REG reg1, offset(basereg) + * CONV_I2/U2 reg1, reg2 + */ + if (last_ins && X86_IS_BYTE_REG (last_ins->sreg1) && + (last_ins->opcode == OP_STOREI1_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? CEE_CONV_I1 : CEE_CONV_U1; + ins->sreg1 = last_ins->sreg1; + } + break; + case OP_LOADU2_MEMBASE: + case OP_LOADI2_MEMBASE: + /* + * OP_STORE_MEMBASE_REG reg1, offset(basereg) + * OP_LOAD_MEMBASE offset(basereg), reg2 + * --> + * OP_STORE_MEMBASE_REG reg1, offset(basereg) + * CONV_I2/U2 reg1, reg2 + */ + if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? CEE_CONV_I2 : CEE_CONV_U2; + ins->sreg1 = last_ins->sreg1; + } + break; + case CEE_CONV_I4: + case CEE_CONV_U4: + case OP_ICONV_TO_I4: + case OP_MOVE: + /* + * Removes: + * + * OP_MOVE reg, reg + */ + if (ins->dreg == ins->sreg1) { + if (last_ins) + last_ins->next = ins->next; + ins = ins->next; + continue; + } + /* + * Removes: + * + * OP_MOVE sreg, dreg + * OP_MOVE dreg, sreg + */ + if (last_ins && last_ins->opcode == OP_MOVE && + ins->sreg1 == last_ins->dreg && + ins->dreg == last_ins->sreg1) { + last_ins->next = ins->next; + ins = ins->next; + continue; + } + break; + case OP_X86_PUSH_MEMBASE: + if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG || + last_ins->opcode == OP_STORE_MEMBASE_REG) && + ins->inst_basereg == last_ins->inst_destbasereg && + ins->inst_offset == last_ins->inst_offset) { + ins->opcode = OP_X86_PUSH; + ins->sreg1 = last_ins->sreg1; + } + break; + } + last_ins = ins; + ins = ins->next; + } + bb->last_ins = last_ins; +} + static const int branch_cc_table [] = { X86_CC_EQ, X86_CC_GE, X86_CC_GT, X86_CC_LE, X86_CC_LT, @@ -1473,12 +1681,25 @@ branch_cc_table [] = { X86_CC_O, X86_CC_NO, X86_CC_C, X86_CC_NC }; -static const char*const * ins_spec = x86_desc; +/* Maps CMP_... constants to X86_CC_... constants */ +static const int +cc_table [] = { + X86_CC_EQ, X86_CC_NE, X86_CC_LE, X86_CC_GE, X86_CC_LT, X86_CC_GT, + X86_CC_LE, X86_CC_GE, X86_CC_LT, X86_CC_GT +}; + +static const int +cc_signed_table [] = { + TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, + FALSE, FALSE, FALSE, FALSE +}; -/*#include "cprop.c"*/ void mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) { + if (cfg->opt & MONO_OPT_PEEPHOLE) + peephole_pass_1 (cfg, bb); + mono_local_regalloc (cfg, bb); } @@ -1638,7 +1859,7 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code) case OP_VCALL: case OP_VCALL_REG: case OP_VCALL_MEMBASE: - cinfo = get_call_info (((MonoCallInst*)ins)->signature, FALSE); + cinfo = get_call_info (cfg->mempool, ((MonoCallInst*)ins)->signature, FALSE); if (cinfo->ret.storage == ArgValuetypeInReg) { /* Pop the destination address from the stack */ x86_pop_reg (code, X86_ECX); @@ -1656,7 +1877,6 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code) } } } - g_free (cinfo); default: break; } @@ -1702,6 +1922,53 @@ emit_tls_get (guint8* code, int dreg, int tls_offset) return code; } +/* + * emit_load_volatile_arguments: + * + * Load volatile arguments from the stack to the original input registers. + * Required before a tail call. + */ +static guint8* +emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code) +{ + MonoMethod *method = cfg->method; + MonoMethodSignature *sig; + MonoInst *inst; + CallInfo *cinfo; + guint32 i; + + /* FIXME: Generate intermediate code instead */ + + sig = mono_method_signature (method); + + cinfo = get_call_info (cfg->mempool, sig, FALSE); + + /* This is the opposite of the code in emit_prolog */ + + for (i = 0; i < sig->param_count + sig->hasthis; ++i) { + ArgInfo *ainfo = cinfo->args + i; + MonoType *arg_type; + inst = cfg->args [i]; + + if (sig->hasthis && (i == 0)) + arg_type = &mono_defaults.object_class->byval_arg; + else + arg_type = sig->params [i - sig->hasthis]; + + /* + * On x86, the arguments are either in their original stack locations, or in + * global regs. + */ + if (inst->opcode == OP_REGVAR) { + g_assert (ainfo->storage == ArgOnStack); + + x86_mov_membase_reg (code, X86_EBP, inst->inst_offset, inst->dreg, 4); + } + } + + return code; +} + #define REAL_PRINT_REG(text,reg) \ mono_assert (reg >= 0); \ x86_push_reg (code, X86_EAX); \ @@ -1770,7 +2037,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) while (ins) { offset = code - cfg->native_code; - max_len = ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN]; + max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN]; if (offset > (cfg->code_size - max_len - 16)) { cfg->code_size *= 2; @@ -1911,7 +2178,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_X86_MUL_MEMBASE: x86_imul_reg_membase (code, ins->sreg1, ins->sreg2, ins->inst_offset); break; - case CEE_BREAK: + case OP_BREAK: x86_breakpoint (code); break; case OP_ADDCC: @@ -1981,6 +2248,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) x86_alu_reg_imm (code, X86_OR, ins->sreg1, ins->inst_imm); break; case CEE_XOR: + case OP_IXOR: x86_alu_reg_reg (code, X86_XOR, ins->sreg1, ins->sreg2); break; case OP_XOR_IMM: @@ -2235,7 +2503,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) break; case CEE_CONV_U4: g_assert_not_reached (); - case CEE_JMP: { + case OP_JMP: { /* * Note: this 'frame destruction' logic is useful for tail calls, too. * Keep in sync with the code in emit_epilog. @@ -2250,6 +2518,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) g_assert (!cfg->method->save_lmf); + code = emit_load_volatile_arguments (cfg, code); + if (cfg->used_int_regs & (1 << X86_EBX)) pos -= 4; if (cfg->used_int_regs & (1 << X86_EDI)) @@ -2391,15 +2661,15 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) break; case OP_LOCALLOC: /* keep alignment */ - x86_alu_reg_imm (code, X86_ADD, ins->sreg1, MONO_ARCH_FRAME_ALIGNMENT - 1); - x86_alu_reg_imm (code, X86_AND, ins->sreg1, ~(MONO_ARCH_FRAME_ALIGNMENT - 1)); + x86_alu_reg_imm (code, X86_ADD, ins->sreg1, MONO_ARCH_LOCALLOC_ALIGNMENT - 1); + x86_alu_reg_imm (code, X86_AND, ins->sreg1, ~(MONO_ARCH_LOCALLOC_ALIGNMENT - 1)); code = mono_emit_stack_alloc (code, ins); x86_mov_reg_reg (code, ins->dreg, X86_ESP, 4); break; case CEE_RET: x86_ret (code); break; - case CEE_THROW: { + case OP_THROW: { x86_push_reg (code, ins->sreg1); code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_arch_throw_exception"); @@ -2425,7 +2695,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_LABEL: ins->inst_c0 = code - cfg->native_code; break; - case CEE_BR: + case OP_BR: //g_print ("target: %p, next: %p, curr: %p, last: %p\n", ins->inst_target_bb, bb->next_bb, ins, bb->last_ins); //if ((ins->inst_target_bb == bb->next_bb) && ins == bb->last_ins) //break; @@ -2457,27 +2727,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) x86_jump_reg (code, ins->sreg1); break; case OP_CEQ: - x86_set_reg (code, X86_CC_EQ, ins->dreg, TRUE); - x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); - break; case OP_CLT: - x86_set_reg (code, X86_CC_LT, ins->dreg, TRUE); - x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); - break; case OP_CLT_UN: - x86_set_reg (code, X86_CC_LT, ins->dreg, FALSE); - x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); - break; case OP_CGT: - x86_set_reg (code, X86_CC_GT, ins->dreg, TRUE); - x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); - break; case OP_CGT_UN: - x86_set_reg (code, X86_CC_GT, ins->dreg, FALSE); - x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); - break; case OP_CNE: - x86_set_reg (code, X86_CC_NE, ins->dreg, TRUE); + x86_set_reg (code, cc_table [mono_opcode_to_cond (ins->opcode)], ins->dreg, cc_signed_table [mono_opcode_to_cond (ins->opcode)]); x86_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); break; case OP_COND_EXC_EQ: @@ -2490,6 +2745,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_COND_EXC_GE_UN: case OP_COND_EXC_LE: case OP_COND_EXC_LE_UN: + EMIT_COND_SYSTEM_EXCEPTION (cc_table [mono_opcode_to_cond (ins->opcode)], cc_signed_table [mono_opcode_to_cond (ins->opcode)], ins->inst_p1); + break; case OP_COND_EXC_OV: case OP_COND_EXC_NO: case OP_COND_EXC_C: @@ -2506,7 +2763,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case CEE_BGE_UN: case CEE_BLE: case CEE_BLE_UN: - EMIT_COND_BRANCH (ins, branch_cc_table [ins->opcode - CEE_BEQ], (ins->opcode < CEE_BNE_UN)); + EMIT_COND_BRANCH (ins, cc_table [mono_opcode_to_cond (ins->opcode)], cc_signed_table [mono_opcode_to_cond (ins->opcode)]); break; /* floating point opcodes */ @@ -2960,7 +3217,18 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_FBGT: case OP_FBGT_UN: if (cfg->opt & MONO_OPT_FCMOV) { - EMIT_COND_BRANCH (ins, X86_CC_LT, FALSE); + if (ins->opcode == OP_FBGT) { + guchar *br1; + + /* skip branch if C1=1 */ + br1 = code; + x86_branch8 (code, X86_CC_P, 0, FALSE); + /* branch if (C0 | C3) = 1 */ + EMIT_COND_BRANCH (ins, X86_CC_LT, FALSE); + x86_patch (br1, code); + } else { + EMIT_COND_BRANCH (ins, X86_CC_LT, FALSE); + } break; } x86_alu_reg_imm (code, X86_CMP, X86_EAX, X86_FP_C0); @@ -3030,7 +3298,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) x86_alu_reg_imm (code, X86_CMP, X86_EAX, X86_FP_C0); EMIT_COND_BRANCH (ins, X86_CC_NE, FALSE); break; - case CEE_CKFINITE: { + case OP_CKFINITE: { x86_push_reg (code, X86_EAX); x86_fxam (code); x86_fnstsw (code); @@ -3295,32 +3563,50 @@ mono_arch_emit_prolog (MonoCompile *cfg) x86_push_reg (code, X86_EDI); x86_push_reg (code, X86_EBX); - /* save method info */ - x86_push_imm (code, method); + if ((lmf_tls_offset != -1) && !is_win32 && !optimize_for_xen) { + /* + * Optimized version which uses the mono_lmf TLS variable instead of indirection + * through the mono_lmf_addr TLS variable. + */ + /* %eax = previous_lmf */ + x86_prefix (code, X86_GS_PREFIX); + x86_mov_reg_mem (code, X86_EAX, lmf_tls_offset, 4); + /* skip method_info + lmf */ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + /* push previous_lmf */ + x86_push_reg (code, X86_EAX); + /* new lmf = ESP */ + x86_prefix (code, X86_GS_PREFIX); + x86_mov_mem_reg (code, lmf_tls_offset, X86_ESP, 4); + } else { + /* get the address of lmf for the current thread */ + /* + * This is performance critical so we try to use some tricks to make + * it fast. + */ - /* get the address of lmf for the current thread */ - /* - * This is performance critical so we try to use some tricks to make - * it fast. - */ - if (lmf_tls_offset != -1) { - /* Load lmf quicky using the GS register */ - code = emit_tls_get (code, X86_EAX, lmf_tls_offset); + if (lmf_addr_tls_offset != -1) { + /* Load lmf quicky using the GS register */ + code = emit_tls_get (code, X86_EAX, lmf_addr_tls_offset); #ifdef PLATFORM_WIN32 - /* The TLS key actually contains a pointer to the MonoJitTlsData structure */ - /* FIXME: Add a separate key for LMF to avoid this */ - x86_alu_reg_imm (code, X86_ADD, X86_EAX, G_STRUCT_OFFSET (MonoJitTlsData, lmf)); + /* The TLS key actually contains a pointer to the MonoJitTlsData structure */ + /* FIXME: Add a separate key for LMF to avoid this */ + x86_alu_reg_imm (code, X86_ADD, X86_EAX, G_STRUCT_OFFSET (MonoJitTlsData, lmf)); #endif - } else { - code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_get_lmf_addr"); - } + } else { + code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_get_lmf_addr"); + } - /* push lmf */ - x86_push_reg (code, X86_EAX); - /* push *lfm (previous_lmf) */ - x86_push_membase (code, X86_EAX, 0); - /* *(lmf) = ESP */ - x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4); + /* Skip method info */ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4); + + /* push lmf */ + x86_push_reg (code, X86_EAX); + /* push *lfm (previous_lmf) */ + x86_push_membase (code, X86_EAX, 0); + /* *(lmf) = ESP */ + x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4); + } } else { if (cfg->used_int_regs & (1 << X86_EBX)) { @@ -3397,7 +3683,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) if (ins->opcode == OP_LABEL) ins->inst_c1 = max_offset; - max_offset += ((guint8 *)ins_spec [ins->opcode])[MONO_INST_LEN]; + max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN]; ins = ins->next; } } @@ -3411,7 +3697,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) pos = 0; for (i = 0; i < sig->param_count + sig->hasthis; ++i) { - inst = cfg->varinfo [pos]; + inst = cfg->args [pos]; if (inst->opcode == OP_REGVAR) { x86_mov_reg_membase (code, inst->dreg, X86_EBP, inst->inst_offset, 4); if (cfg->verbose_level > 2) @@ -3453,33 +3739,46 @@ mono_arch_emit_epilog (MonoCompile *cfg) if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE); - /* the code restoring the registers must be kept in sync with CEE_JMP */ + /* the code restoring the registers must be kept in sync with OP_JMP */ pos = 0; if (method->save_lmf) { gint32 prev_lmf_reg; gint32 lmf_offset = -sizeof (MonoLMF); - /* Find a spare register */ - switch (sig->ret->type) { - case MONO_TYPE_I8: - case MONO_TYPE_U8: - prev_lmf_reg = X86_EDI; - cfg->used_int_regs |= (1 << X86_EDI); - break; - default: - prev_lmf_reg = X86_EDX; - break; - } + if ((lmf_tls_offset != -1) && !is_win32 && !optimize_for_xen) { + /* + * Optimized version which uses the mono_lmf TLS variable instead of indirection + * through the mono_lmf_addr TLS variable. + */ + /* reg = previous_lmf */ + x86_mov_reg_membase (code, X86_ECX, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), 4); + + /* lmf = previous_lmf */ + x86_prefix (code, X86_GS_PREFIX); + x86_mov_mem_reg (code, lmf_tls_offset, X86_ECX, 4); + } else { + /* Find a spare register */ + switch (sig->ret->type) { + case MONO_TYPE_I8: + case MONO_TYPE_U8: + prev_lmf_reg = X86_EDI; + cfg->used_int_regs |= (1 << X86_EDI); + break; + default: + prev_lmf_reg = X86_EDX; + break; + } - /* reg = previous_lmf */ - x86_mov_reg_membase (code, prev_lmf_reg, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), 4); + /* reg = previous_lmf */ + x86_mov_reg_membase (code, prev_lmf_reg, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), 4); - /* ecx = lmf */ - x86_mov_reg_membase (code, X86_ECX, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), 4); + /* ecx = lmf */ + x86_mov_reg_membase (code, X86_ECX, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), 4); - /* *(lmf) = previous_lmf */ - x86_mov_membase_reg (code, X86_ECX, 0, prev_lmf_reg, 4); + /* *(lmf) = previous_lmf */ + x86_mov_membase_reg (code, X86_ECX, 0, prev_lmf_reg, 4); + } /* restore caller saved regs */ if (cfg->used_int_regs & (1 << X86_EBX)) { @@ -3520,7 +3819,7 @@ mono_arch_emit_epilog (MonoCompile *cfg) } /* Load returned vtypes into registers if needed */ - cinfo = get_call_info (sig, FALSE); + cinfo = get_call_info (cfg->mempool, sig, FALSE); if (cinfo->ret.storage == ArgValuetypeInReg) { for (quad = 0; quad < 2; quad ++) { switch (cinfo->ret.pair_storage [quad]) { @@ -3557,8 +3856,6 @@ mono_arch_emit_epilog (MonoCompile *cfg) else x86_ret (code); - g_free (cinfo); - cfg->code_len = code - cfg->native_code; g_assert (cfg->code_len < cfg->code_size); @@ -3643,7 +3940,7 @@ mono_arch_emit_exceptions (MonoCompile *cfg) exc_throw_start [nthrows] = code; } - x86_push_imm (code, exc_class->type_token); + x86_push_imm (code, exc_class->type_token - MONO_TOKEN_TYPE_DEF); patch_info->data.name = "mono_arch_throw_corlib_exception"; patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD; patch_info->ip.i = code - cfg->native_code; @@ -3716,6 +4013,7 @@ mono_arch_setup_jit_tls_data (MonoJitTlsData *tls) tls_offset_inited = TRUE; appdomain_tls_offset = mono_domain_get_tls_offset (); lmf_tls_offset = mono_get_lmf_tls_offset (); + lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset (); thread_tls_offset = mono_thread_get_tls_offset (); #endif } @@ -3731,7 +4029,7 @@ void mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg) { MonoCallInst *call = (MonoCallInst*)inst; - CallInfo *cinfo = get_call_info (inst->signature, FALSE); + CallInfo *cinfo = get_call_info (cfg->mempool, inst->signature, FALSE); /* add the this argument */ if (this_reg != -1) { @@ -3785,8 +4083,6 @@ mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_re mono_bblock_add_inst (cfg->cbb, vtarg); } } - - g_free (cinfo); } MonoInst* @@ -3978,22 +4274,94 @@ mono_arch_get_vcall_slot_addr (guint8 *code, gpointer *regs) return (gpointer*)(((gint32)(regs [reg])) + disp); } -gpointer* -mono_arch_get_delegate_method_ptr_addr (guint8* code, gpointer *regs) +gpointer +mono_arch_get_this_arg_from_call (MonoMethodSignature *sig, gssize *regs, guint8 *code) { - guint8 reg = 0; - gint32 disp = 0; + guint32 esp = regs [X86_ESP]; + CallInfo *cinfo; + gpointer res; - code -= 7; - if ((code [0] == 0x8b) && (x86_modrm_mod (code [1]) == 3) && (x86_modrm_reg (code [1]) == X86_EAX) && (code [2] == 0x8b) && (code [3] == 0x40) && (code [5] == 0xff) && (code [6] == 0xd0)) { - reg = x86_modrm_rm (code [1]); - disp = code [4]; + cinfo = get_call_info (NULL, sig, FALSE); - if (reg == X86_EAX) - return NULL; - else - return (gpointer*)(((gint32)(regs [reg])) + disp); + /* + * The stack looks like: + * + * + * + * + * <4 pointers pushed by mono_arch_create_trampoline_code ()> + */ + res = (((MonoObject**)esp) [5 + (cinfo->args [0].offset / 4)]); + g_free (cinfo); + return res; +} + +gpointer +mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target) +{ + guint8 *code, *start; + MonoDomain *domain = mono_domain_get (); + + /* FIXME: Support more cases */ + if (MONO_TYPE_ISSTRUCT (sig->ret)) + return NULL; + + /* + * The stack contains: + * + * + */ + + if (has_target) { + mono_domain_lock (domain); + start = code = mono_code_manager_reserve (domain->code_mp, 64); + mono_domain_unlock (domain); + + /* Replace the this argument with the target */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); + x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoDelegate, target), 4); + x86_mov_membase_reg (code, X86_ESP, 4, X86_ECX, 4); + x86_jump_membase (code, X86_EAX, G_STRUCT_OFFSET (MonoDelegate, method_ptr)); + + g_assert ((code - start) < 64); + } else { + if (sig->param_count == 0) { + mono_domain_lock (domain); + start = code = mono_code_manager_reserve (domain->code_mp, 32 + (sig->param_count * 8)); + mono_domain_unlock (domain); + + x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); + x86_jump_membase (code, X86_EAX, G_STRUCT_OFFSET (MonoDelegate, method_ptr)); + } else { + /* + * The code below does not work in the presence of exceptions, since it + * creates a new frame. + */ + start = NULL; +#if 0 + for (i = 0; i < sig->param_count; ++i) + if (!mono_is_regsize_var (sig->params [i])) + return NULL; + + mono_domain_lock (domain); + start = code = mono_code_manager_reserve (domain->code_mp, 32 + (sig->param_count * 8)); + mono_domain_unlock (domain); + + /* Load this == delegate */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); + + /* Push arguments in opposite order, taking changes in ESP into account */ + for (i = 0; i < sig->param_count; ++i) + x86_push_membase (code, X86_ESP, 4 + (sig->param_count * 4)); + + /* Call the delegate */ + x86_call_membase (code, X86_EAX, G_STRUCT_OFFSET (MonoDelegate, method_ptr)); + if (sig->param_count > 0) + x86_alu_reg_imm (code, X86_ADD, X86_ESP, sig->param_count * 4); + x86_ret (code); +#endif + } } - return NULL; + return start; }