X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini-x86.c;h=e649ab21388b2e50c9a5bc7b7f41eca1d2120cfb;hb=4c27a8f878f97b833eeb20faa5c5a0b1e34f4121;hp=6da3071a44ad6c3c36387fd680bf120f9101a832;hpb=d47b446ee760ecc82a65d70c6c7eab7c11a2cf22;p=mono.git diff --git a/mono/mini/mini-x86.c b/mono/mini/mini-x86.c index 6da3071a44a..e649ab21388 100644 --- a/mono/mini/mini-x86.c +++ b/mono/mini/mini-x86.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "trace.h" @@ -44,6 +45,11 @@ static gboolean is_win32 = TRUE; static gboolean is_win32 = FALSE; #endif +/* This mutex protects architecture specific caches */ +#define mono_mini_arch_lock() EnterCriticalSection (&mini_arch_mutex) +#define mono_mini_arch_unlock() LeaveCriticalSection (&mini_arch_mutex) +static CRITICAL_SECTION mini_arch_mutex; + #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) #define ARGS_OFFSET 8 @@ -57,14 +63,19 @@ static gboolean is_win32 = FALSE; #define NOT_IMPLEMENTED g_assert_not_reached () +MonoBreakpointInfo +mono_breakpoint_info [MONO_BREAKPOINT_ARRAY_SIZE]; + const char* -mono_arch_regname (int reg) { +mono_arch_regname (int reg) +{ switch (reg) { case X86_EAX: return "%eax"; case X86_EBX: return "%ebx"; case X86_ECX: return "%ecx"; case X86_EDX: return "%edx"; - case X86_ESP: return "%esp"; case X86_EBP: return "%ebp"; + case X86_ESP: return "%esp"; + case X86_EBP: return "%ebp"; case X86_EDI: return "%edi"; case X86_ESI: return "%esi"; } @@ -72,8 +83,28 @@ mono_arch_regname (int reg) { } const char* -mono_arch_fregname (int reg) { - return "unknown"; +mono_arch_fregname (int reg) +{ + switch (reg) { + case 0: + return "%fr0"; + case 1: + return "%fr1"; + case 2: + return "%fr2"; + case 3: + return "%fr3"; + case 4: + return "%fr4"; + case 5: + return "%fr5"; + case 6: + return "%fr6"; + case 7: + return "%fr7"; + default: + return "unknown"; + } } typedef enum { @@ -169,7 +200,7 @@ add_float (guint32 *gr, guint32 *stack_size, ArgInfo *ainfo, gboolean is_double) static void -add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, +add_valuetype (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, gboolean is_return, guint32 *gr, guint32 *fr, guint32 *stack_size) { @@ -180,7 +211,7 @@ add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, if (sig->pinvoke) size = mono_type_native_stack_size (&klass->byval_arg, NULL); else - size = mono_type_stack_size (&klass->byval_arg, NULL); + size = mini_type_stack_size (gsctx, &klass->byval_arg, NULL); #ifdef SMALL_STRUCTS_IN_REGS if (sig->pinvoke && is_return) { @@ -234,13 +265,14 @@ add_valuetype (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type, * For x86 win32, see ???. */ static CallInfo* -get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) +get_call_info (MonoCompile *cfg, MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) { guint32 i, gr, fr; MonoType *ret_type; int n = sig->hasthis + sig->param_count; guint32 stack_size = 0; CallInfo *cinfo; + MonoGenericSharingContext *gsctx = cfg ? cfg->generic_sharing_context : NULL; if (mp) cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n)); @@ -253,6 +285,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) /* return value */ { ret_type = mono_type_get_underlying_type (sig->ret); + ret_type = mini_get_basic_type_from_generic (gsctx, ret_type); switch (ret_type->type) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_I1: @@ -295,7 +328,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) case MONO_TYPE_VALUETYPE: { guint32 tmp_gr = 0, tmp_fr = 0, tmp_stacksize = 0; - add_valuetype (sig, &cinfo->ret, sig->ret, TRUE, &tmp_gr, &tmp_fr, &tmp_stacksize); + add_valuetype (gsctx, sig, &cinfo->ret, sig->ret, TRUE, &tmp_gr, &tmp_fr, &tmp_stacksize); if (cinfo->ret.storage == ArgOnStack) /* The caller passes the address where the value is stored */ add_general (&gr, &stack_size, &cinfo->ret); @@ -348,6 +381,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) continue; } ptype = mono_type_get_underlying_type (sig->params [i]); + ptype = mini_get_basic_type_from_generic (gsctx, ptype); switch (ptype->type) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_I1: @@ -381,7 +415,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) } /* Fall through */ case MONO_TYPE_VALUETYPE: - add_valuetype (sig, ainfo, sig->params [i], FALSE, &gr, &fr, &stack_size); + add_valuetype (gsctx, sig, ainfo, sig->params [i], FALSE, &gr, &fr, &stack_size); break; case MONO_TYPE_TYPEDBYREF: stack_size += sizeof (MonoTypedRef); @@ -444,7 +478,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit int offset = 8; CallInfo *cinfo; - cinfo = get_call_info (NULL, csig, FALSE); + cinfo = get_call_info (NULL, NULL, csig, FALSE); if (MONO_TYPE_ISSTRUCT (csig->ret) && (cinfo->ret.storage == ArgOnStack)) { frame_size += sizeof (gpointer); @@ -466,7 +500,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit size = mono_type_native_stack_size (csig->params [k], &align); else { int ialign; - size = mono_type_stack_size (csig->params [k], &ialign); + size = mini_type_stack_size (NULL, csig->params [k], &ialign); align = ialign; } @@ -594,6 +628,24 @@ mono_arch_cpu_init (void) #endif } +/* + * Initialize architecture specific code. + */ +void +mono_arch_init (void) +{ + InitializeCriticalSection (&mini_arch_mutex); +} + +/* + * Cleanup architecture specific code. + */ +void +mono_arch_cleanup (void) +{ + DeleteCriticalSection (&mini_arch_mutex); +} + /* * This function returns the optimizations supported on this cpu. */ @@ -614,6 +666,10 @@ mono_arch_cpu_optimizazions (guint32 *exclude_mask) *exclude_mask |= MONO_OPT_FCMOV; } else *exclude_mask |= MONO_OPT_CMOV; + if (edx & (1 << 26)) + opts |= MONO_OPT_SSE2; + else + *exclude_mask |= MONO_OPT_SSE2; } return opts; } @@ -750,7 +806,7 @@ mono_arch_allocate_vars (MonoCompile *cfg) header = mono_method_get_header (cfg->method); sig = mono_method_signature (cfg->method); - cinfo = get_call_info (cfg->mempool, sig, FALSE); + cinfo = get_call_info (cfg, cfg->mempool, sig, FALSE); cfg->frame_reg = MONO_ARCH_BASEREG; offset = 0; @@ -856,7 +912,7 @@ mono_arch_create_vars (MonoCompile *cfg) sig = mono_method_signature (cfg->method); - cinfo = get_call_info (cfg->mempool, sig, FALSE); + cinfo = get_call_info (cfg, cfg->mempool, sig, FALSE); if (cinfo->ret.storage == ArgValuetypeInReg) cfg->ret_var_is_local = TRUE; @@ -898,6 +954,33 @@ emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call) call->out_args = arg; } +/* + * It is expensive to adjust esp for each individual fp argument pushed on the stack + * so we try to do it just once when we have multiple fp arguments in a row. + * We don't use this mechanism generally because for int arguments the generated code + * is slightly bigger and new generation cpus optimize away the dependency chains + * created by push instructions on the esp value. + * fp_arg_setup is the first argument in the execution sequence where the esp register + * is modified. + */ +static int +collect_fp_stack_space (MonoMethodSignature *sig, int start_arg, int *fp_arg_setup) +{ + int fp_space = 0; + MonoType *t; + + for (; start_arg < sig->param_count; ++start_arg) { + t = mono_type_get_underlying_type (sig->params [start_arg]); + if (!t->byref && t->type == MONO_TYPE_R8) { + fp_space += sizeof (double); + *fp_arg_setup = start_arg; + } else { + break; + } + } + return fp_space; +} + /* * take the arguments and generate the arch-specific * instructions to properly call the function in call. @@ -911,11 +994,12 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int i, n; CallInfo *cinfo; int sentinelpos = 0; + int fp_args_space = 0, fp_args_offset = 0, fp_arg_setup = -1; sig = call->signature; n = sig->param_count + sig->hasthis; - cinfo = get_call_info (cfg->mempool, sig, FALSE); + cinfo = get_call_info (cfg, cfg->mempool, sig, FALSE); if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG)) sentinelpos = sig->sentinelpos + (is_virtual ? 1 : 0); @@ -961,7 +1045,7 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, size = mono_type_native_stack_size (&in->klass->byval_arg, &align); else { int ialign; - size = mono_type_stack_size (&in->klass->byval_arg, &ialign); + size = mini_type_stack_size (cfg->generic_sharing_context, &in->klass->byval_arg, &ialign); align = ialign; } arg->opcode = OP_OUTARG_VT; @@ -974,11 +1058,30 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, case ArgOnStack: arg->opcode = OP_OUTARG; if (!t->byref) { - if (t->type == MONO_TYPE_R4) + if (t->type == MONO_TYPE_R4) { arg->opcode = OP_OUTARG_R4; - else - if (t->type == MONO_TYPE_R8) - arg->opcode = OP_OUTARG_R8; + } else if (t->type == MONO_TYPE_R8) { + arg->opcode = OP_OUTARG_R8; + /* we store in the upper bits of backen.arg_info the needed + * esp adjustment and in the lower bits the offset from esp + * where the arg needs to be stored + */ + if (!fp_args_space) { + fp_args_space = collect_fp_stack_space (sig, i - sig->hasthis, &fp_arg_setup); + fp_args_offset = fp_args_space; + } + arg->backend.arg_info = fp_args_space - fp_args_offset; + fp_args_offset -= sizeof (double); + if (i - sig->hasthis == fp_arg_setup) { + arg->backend.arg_info |= fp_args_space << 16; + } + if (fp_args_offset == 0) { + /* the allocated esp stack is finished: + * prepare for an eventual second run of fp args + */ + fp_args_space = 0; + } + } } break; default: @@ -1706,6 +1809,21 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) static unsigned char* emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int size, gboolean is_signed) { +#define XMM_TEMP_REG 0 + if (cfg->opt & MONO_OPT_SSE2 && size < 8) { + /* optimize by assigning a local var for this use so we avoid + * the stack manipulations */ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); + x86_fst_membase (code, X86_ESP, 0, TRUE, TRUE); + x86_movsd_reg_membase (code, XMM_TEMP_REG, X86_ESP, 0); + x86_cvttsd2si (code, dreg, XMM_TEMP_REG); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8); + if (size == 1) + x86_widen_reg (code, dreg, dreg, is_signed, FALSE); + else if (size == 2) + x86_widen_reg (code, dreg, dreg, is_signed, TRUE); + return code; + } x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4); x86_fnstcw_membase(code, X86_ESP, 0); x86_mov_reg_membase (code, dreg, X86_ESP, 0, 2); @@ -1859,7 +1977,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 (cfg->mempool, ((MonoCallInst*)ins)->signature, FALSE); + cinfo = get_call_info (cfg, cfg->mempool, ((MonoCallInst*)ins)->signature, FALSE); if (cinfo->ret.storage == ArgValuetypeInReg) { /* Pop the destination address from the stack */ x86_pop_reg (code, X86_ECX); @@ -1941,7 +2059,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code) sig = mono_method_signature (method); - cinfo = get_call_info (cfg->mempool, sig, FALSE); + cinfo = get_call_info (cfg, cfg->mempool, sig, FALSE); /* This is the opposite of the code in emit_prolog */ @@ -2039,14 +2157,15 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN]; - if (offset > (cfg->code_size - max_len - 16)) { + if (G_UNLIKELY (offset > (cfg->code_size - max_len - 16))) { cfg->code_size *= 2; cfg->native_code = g_realloc (cfg->native_code, cfg->code_size); code = cfg->native_code + offset; mono_jit_stats.code_reallocs++; } - mono_debug_record_line_number (cfg, ins, offset); + if (cfg->debug_info) + mono_debug_record_line_number (cfg, ins, offset); switch (ins->opcode) { case OP_BIGMUL: @@ -2107,9 +2226,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) x86_widen_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset, TRUE, TRUE); break; case CEE_CONV_I1: + case OP_SEXT_I1: x86_widen_reg (code, ins->dreg, ins->sreg1, TRUE, FALSE); break; case CEE_CONV_I2: + case OP_SEXT_I2: x86_widen_reg (code, ins->dreg, ins->sreg1, TRUE, TRUE); break; case CEE_CONV_U1: @@ -2362,12 +2483,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case CEE_NEG: x86_neg_reg (code, ins->sreg1); break; - case OP_SEXT_I1: - x86_widen_reg (code, ins->dreg, ins->sreg1, TRUE, FALSE); - break; - case OP_SEXT_I2: - x86_widen_reg (code, ins->dreg, ins->sreg1, TRUE, TRUE); - break; + case CEE_MUL: x86_imul_reg_reg (code, ins->sreg1, ins->sreg2); break; @@ -2692,6 +2808,25 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12); #endif break; + case OP_START_HANDLER: { + MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region); + x86_mov_membase_reg (code, spvar->inst_basereg, spvar->inst_offset, X86_ESP, 4); + break; + } + case OP_ENDFINALLY: { + MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region); + x86_mov_reg_membase (code, X86_ESP, spvar->inst_basereg, spvar->inst_offset, 4); + x86_ret (code); + break; + } + case OP_ENDFILTER: { + MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region); + x86_mov_reg_membase (code, X86_ESP, spvar->inst_basereg, spvar->inst_offset, 4); + /* The local allocator will put the result into EAX */ + x86_ret (code); + break; + } + case OP_LABEL: ins->inst_c0 = code - cfg->native_code; break; @@ -3515,7 +3650,11 @@ mono_arch_emit_prolog (MonoCompile *cfg) int alloc_size, pos, max_offset, i; guint8 *code; - cfg->code_size = MAX (mono_method_get_header (method)->code_size * 4, 256); + cfg->code_size = MAX (mono_method_get_header (method)->code_size * 4, 1024); + + if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) + cfg->code_size += 512; + code = cfg->native_code = g_malloc (cfg->code_size); x86_push_reg (code, X86_EBP); @@ -3525,14 +3664,19 @@ mono_arch_emit_prolog (MonoCompile *cfg) pos = 0; if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) { - /* Might need to attach the thread to the JIT */ - if (lmf_tls_offset != -1) { - guint8 *buf; + /* Might need to attach the thread to the JIT or change the domain for the callback */ + if (appdomain_tls_offset != -1 && lmf_tls_offset != -1) { + guint8 *buf, *no_domain_branch; + code = emit_tls_get (code, X86_EAX, appdomain_tls_offset); + x86_alu_reg_imm (code, X86_CMP, X86_EAX, GPOINTER_TO_UINT (cfg->domain)); + no_domain_branch = code; + x86_branch8 (code, X86_CC_NE, 0, 0); code = emit_tls_get ( code, X86_EAX, lmf_tls_offset); x86_test_reg_reg (code, X86_EAX, X86_EAX); buf = code; x86_branch8 (code, X86_CC_NE, 0, 0); + x86_patch (no_domain_branch, code); x86_push_imm (code, cfg->domain); code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_jit_thread_attach"); x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); @@ -3571,8 +3715,8 @@ mono_arch_emit_prolog (MonoCompile *cfg) /* %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); + /* skip esp + method_info + lmf */ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 12); /* push previous_lmf */ x86_push_reg (code, X86_EAX); /* new lmf = ESP */ @@ -3597,8 +3741,8 @@ mono_arch_emit_prolog (MonoCompile *cfg) code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_get_lmf_addr"); } - /* Skip method info */ - x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4); + /* Skip esp + method info */ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8); /* push lmf */ x86_push_reg (code, X86_EAX); @@ -3724,9 +3868,6 @@ mono_arch_emit_epilog (MonoCompile *cfg) if (cfg->method->save_lmf) max_epilog_size += 128; - - if (mono_jit_trace_calls != NULL) - max_epilog_size += 50; while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) { cfg->code_size *= 2; @@ -3819,7 +3960,7 @@ mono_arch_emit_epilog (MonoCompile *cfg) } /* Load returned vtypes into registers if needed */ - cinfo = get_call_info (cfg->mempool, sig, FALSE); + cinfo = get_call_info (cfg, cfg->mempool, sig, FALSE); if (cinfo->ret.storage == ArgValuetypeInReg) { for (quad = 0; quad < 2; quad ++) { switch (cinfo->ret.pair_storage [quad]) { @@ -4029,7 +4170,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 (cfg->mempool, inst->signature, FALSE); + CallInfo *cinfo = get_call_info (cfg, cfg->mempool, inst->signature, FALSE); /* add the this argument */ if (this_reg != -1) { @@ -4085,6 +4226,137 @@ mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_re } } +#ifdef MONO_ARCH_HAVE_IMT + +// Linear handler, the bsearch head compare is shorter +//[2 + 4] x86_alu_reg_imm (code, X86_CMP, ins->sreg1, ins->inst_imm); +//[1 + 1] x86_branch8(inst,cond,imm,is_signed) +// x86_patch(ins,target) +//[1 + 5] x86_jump_mem(inst,mem) + +#define CMP_SIZE 6 +#define BR_SMALL_SIZE 2 +#define BR_LARGE_SIZE 5 +#define JUMP_IMM_SIZE 6 +#define ENABLE_WRONG_METHOD_CHECK 0 + +static int +imt_branch_distance (MonoIMTCheckItem **imt_entries, int start, int target) +{ + int i, distance = 0; + for (i = start; i < target; ++i) + distance += imt_entries [i]->chunk_size; + return distance; +} + +/* + * LOCKING: called with the domain lock held + */ +gpointer +mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count) +{ + int i; + int size = 0; + guint8 *code, *start; + + for (i = 0; i < count; ++i) { + MonoIMTCheckItem *item = imt_entries [i]; + if (item->is_equals) { + if (item->check_target_idx) { + if (!item->compare_done) + item->chunk_size += CMP_SIZE; + item->chunk_size += BR_SMALL_SIZE + JUMP_IMM_SIZE; + } else { + item->chunk_size += JUMP_IMM_SIZE; +#if ENABLE_WRONG_METHOD_CHECK + item->chunk_size += CMP_SIZE + BR_SMALL_SIZE + 1; +#endif + } + } else { + item->chunk_size += CMP_SIZE + BR_LARGE_SIZE; + imt_entries [item->check_target_idx]->compare_done = TRUE; + } + size += item->chunk_size; + } + code = mono_code_manager_reserve (domain->code_mp, size); + start = code; + for (i = 0; i < count; ++i) { + MonoIMTCheckItem *item = imt_entries [i]; + item->code_target = code; + if (item->is_equals) { + if (item->check_target_idx) { + if (!item->compare_done) + x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->method); + item->jmp_code = code; + x86_branch8 (code, X86_CC_NE, 0, FALSE); + x86_jump_mem (code, & (vtable->vtable [item->vtable_slot])); + } else { + /* enable the commented code to assert on wrong method */ +#if ENABLE_WRONG_METHOD_CHECK + x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->method); + item->jmp_code = code; + x86_branch8 (code, X86_CC_NE, 0, FALSE); +#endif + x86_jump_mem (code, & (vtable->vtable [item->vtable_slot])); +#if ENABLE_WRONG_METHOD_CHECK + x86_patch (item->jmp_code, code); + x86_breakpoint (code); + item->jmp_code = NULL; +#endif + } + } else { + x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->method); + item->jmp_code = code; + if (x86_is_imm8 (imt_branch_distance (imt_entries, i, item->check_target_idx))) + x86_branch8 (code, X86_CC_GE, 0, FALSE); + else + x86_branch32 (code, X86_CC_GE, 0, FALSE); + } + } + /* patch the branches to get to the target items */ + for (i = 0; i < count; ++i) { + MonoIMTCheckItem *item = imt_entries [i]; + if (item->jmp_code) { + if (item->check_target_idx) { + x86_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target); + } + } + } + + mono_stats.imt_thunks_size += code - start; + g_assert (code - start <= size); + return start; +} + +MonoMethod* +mono_arch_find_imt_method (gpointer *regs, guint8 *code) +{ + return (MonoMethod*) regs [MONO_ARCH_IMT_REG]; +} + +MonoObject* +mono_arch_find_this_argument (gpointer *regs, MonoMethod *method) +{ + MonoMethodSignature *sig = mono_method_signature (method); + CallInfo *cinfo = get_call_info (NULL, NULL, sig, FALSE); + int this_argument_offset; + MonoObject *this_argument; + + /* + * this is the offset of the this arg from esp as saved at the start of + * mono_arch_create_trampoline_code () in tramp-x86.c. + */ + this_argument_offset = 5; + if (MONO_TYPE_ISSTRUCT (sig->ret) && (cinfo->ret.storage == ArgOnStack)) + this_argument_offset++; + + this_argument = * (MonoObject**) (((guint8*) regs [X86_ESP]) + this_argument_offset * sizeof (gpointer)); + + g_free (cinfo); + return this_argument; +} +#endif + MonoInst* mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) { @@ -4118,42 +4390,6 @@ mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethod ins->inst_i1 = args [1]; } #endif - } else if (cmethod->klass == mono_defaults.thread_class && - strcmp (cmethod->name, "MemoryBarrier") == 0) { - MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER); - } else if(cmethod->klass->image == mono_defaults.corlib && - (strcmp (cmethod->klass->name_space, "System.Threading") == 0) && - (strcmp (cmethod->klass->name, "Interlocked") == 0)) { - - if (strcmp (cmethod->name, "Increment") == 0 && fsig->params [0]->type == MONO_TYPE_I4) { - MonoInst *ins_iconst; - - MONO_INST_NEW (cfg, ins, OP_ATOMIC_ADD_NEW_I4); - MONO_INST_NEW (cfg, ins_iconst, OP_ICONST); - ins_iconst->inst_c0 = 1; - - ins->inst_i0 = args [0]; - ins->inst_i1 = ins_iconst; - } else if (strcmp (cmethod->name, "Decrement") == 0 && fsig->params [0]->type == MONO_TYPE_I4) { - MonoInst *ins_iconst; - - MONO_INST_NEW (cfg, ins, OP_ATOMIC_ADD_NEW_I4); - MONO_INST_NEW (cfg, ins_iconst, OP_ICONST); - ins_iconst->inst_c0 = -1; - - ins->inst_i0 = args [0]; - ins->inst_i1 = ins_iconst; - } else if (strcmp (cmethod->name, "Exchange") == 0 && fsig->params [0]->type == MONO_TYPE_I4) { - MONO_INST_NEW (cfg, ins, OP_ATOMIC_EXCHANGE_I4); - - ins->inst_i0 = args [0]; - ins->inst_i1 = args [1]; - } else if (strcmp (cmethod->name, "Add") == 0 && fsig->params [0]->type == MONO_TYPE_I4) { - MONO_INST_NEW (cfg, ins, OP_ATOMIC_ADD_NEW_I4); - - ins->inst_i0 = args [0]; - ins->inst_i1 = args [1]; - } } return ins; @@ -4221,12 +4457,40 @@ mono_arch_get_patch_offset (guint8 *code) } } -gpointer* -mono_arch_get_vcall_slot_addr (guint8 *code, gpointer *regs) +gboolean +mono_breakpoint_clean_code (guint8 *code, guint8 *buf, int size) { + int i; + gboolean can_write = TRUE; + memcpy (buf, code, size); + for (i = 0; i < MONO_BREAKPOINT_ARRAY_SIZE; ++i) { + int idx = mono_breakpoint_info_index [i]; + guint8 *ptr; + if (idx < 1) + continue; + ptr = mono_breakpoint_info [idx].address; + if (ptr >= code && ptr < code + size) { + guint8 saved_byte = mono_breakpoint_info [idx].saved_byte; + can_write = FALSE; + /*g_print ("patching %p with 0x%02x (was: 0x%02x)\n", ptr, saved_byte, buf [ptr - code]);*/ + buf [ptr - code] = saved_byte; + } + } + return can_write; +} + +gpointer +mono_arch_get_vcall_slot (guint8 *code, gpointer *regs, int *displacement) +{ + guint8 buf [8]; guint8 reg = 0; gint32 disp = 0; + mono_breakpoint_clean_code (code - 8, buf, sizeof (buf)); + code = buf + 8; + + *displacement = 0; + /* go to the start of the call instruction * * address_byte = (m << 6) | (o << 3) | reg @@ -4249,6 +4513,15 @@ mono_arch_get_vcall_slot_addr (guint8 *code, gpointer *regs) */ reg = x86_modrm_rm (code [5]); disp = 0; +#ifdef MONO_ARCH_HAVE_IMT + } else if ((code [-2] == 0xba) && (code [3] == 0xff) && (x86_modrm_mod (code [4]) == 1) && (x86_modrm_reg (code [4]) == 2) && ((signed char)code [5] < 0)) { + /* IMT-based interface calls: with MONO_ARCH_IMT_REG == edx + * ba 14 f8 28 08 mov $0x828f814,%edx + * ff 50 fc call *0xfffffffc(%eax) + */ + reg = code [4] & 0x07; + disp = (signed char)code [5]; +#endif } else if ((code [1] != 0xe8) && (code [3] == 0xff) && ((code [4] & 0x18) == 0x10) && ((code [4] >> 6) == 1)) { reg = code [4] & 0x07; disp = (signed char)code [5]; @@ -4271,7 +4544,19 @@ mono_arch_get_vcall_slot_addr (guint8 *code, gpointer *regs) return NULL; } - return (gpointer*)(((gint32)(regs [reg])) + disp); + *displacement = disp; + return regs [reg]; +} + +gpointer* +mono_arch_get_vcall_slot_addr (guint8 *code, gpointer *regs) +{ + gpointer vt; + int displacement; + vt = mono_arch_get_vcall_slot (code, regs, &displacement); + if (!vt) + return NULL; + return (gpointer*)((char*)vt + displacement); } gpointer @@ -4281,7 +4566,7 @@ mono_arch_get_this_arg_from_call (MonoMethodSignature *sig, gssize *regs, guint8 CallInfo *cinfo; gpointer res; - cinfo = get_call_info (NULL, sig, FALSE); + cinfo = get_call_info (NULL, NULL, sig, FALSE); /* * The stack looks like: @@ -4296,11 +4581,15 @@ mono_arch_get_this_arg_from_call (MonoMethodSignature *sig, gssize *regs, guint8 return res; } +#define MAX_ARCH_DELEGATE_PARAMS 10 + gpointer mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target) { guint8 *code, *start; - MonoDomain *domain = mono_domain_get (); + + if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS) + return NULL; /* FIXME: Support more cases */ if (MONO_TYPE_ISSTRUCT (sig->ret)) @@ -4313,9 +4602,14 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe */ if (has_target) { - mono_domain_lock (domain); - start = code = mono_code_manager_reserve (domain->code_mp, 64); - mono_domain_unlock (domain); + static guint8* cached = NULL; + mono_mini_arch_lock (); + if (cached) { + mono_mini_arch_unlock (); + return cached; + } + + start = code = mono_global_codeman_reserve (64); /* Replace the this argument with the target */ x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); @@ -4324,43 +4618,62 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe x86_jump_membase (code, X86_EAX, G_STRUCT_OFFSET (MonoDelegate, method_ptr)); g_assert ((code - start) < 64); + + cached = start; + mono_debug_add_delegate_trampoline (start, code - start); + mono_mini_arch_unlock (); } 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; + static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL}; + int i = 0; + /* 8 for mov_reg and jump, plus 8 for each parameter */ + int code_reserve = 8 + (sig->param_count * 8); + + for (i = 0; i < sig->param_count; ++i) + if (!mono_is_regsize_var (sig->params [i])) + return NULL; + + mono_mini_arch_lock (); + code = cache [sig->param_count]; + if (code) { + mono_mini_arch_unlock (); + return code; + } - mono_domain_lock (domain); - start = code = mono_code_manager_reserve (domain->code_mp, 32 + (sig->param_count * 8)); - mono_domain_unlock (domain); + /* + * The stack contains: + * + * + * + * + * and we need: + * + * + * + * without unbalancing the stack. + * So move each arg up a spot in the stack (overwriting un-needed 'this' arg) + * and leaving original spot of first arg as placeholder in stack so + * when callee pops stack everything works. + */ - /* Load this == delegate */ - x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); + start = code = mono_global_codeman_reserve (code_reserve); - /* 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)); + /* store delegate for access to method_ptr */ + x86_mov_reg_membase (code, X86_ECX, X86_ESP, 4, 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 + /* move args up */ + for (i = 0; i < sig->param_count; ++i) { + x86_mov_reg_membase (code, X86_EAX, X86_ESP, (i+2)*4, 4); + x86_mov_membase_reg (code, X86_ESP, (i+1)*4, X86_EAX, 4); } + + x86_jump_membase (code, X86_ECX, G_STRUCT_OFFSET (MonoDelegate, method_ptr)); + + g_assert ((code - start) < code_reserve); + + cache [sig->param_count] = start; + + mono_debug_add_delegate_trampoline (start, code - start); + mono_mini_arch_unlock (); } return start;