X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini-x86.c;h=de32a0a1fea0e52eab0ddb85324bb8347b45a008;hb=ceaff7fe4d7cd2de06de70d6c9e9432c4eb062f0;hp=5a11ae2ef4a86d5e50562c077bba2707a9b09b18;hpb=ced6bbeeaf32d05cd63730532553ad101a49bcac;p=mono.git diff --git a/mono/mini/mini-x86.c b/mono/mini/mini-x86.c index 5a11ae2ef4a..de32a0a1fea 100644 --- a/mono/mini/mini-x86.c +++ b/mono/mini/mini-x86.c @@ -4,6 +4,7 @@ * Authors: * Paolo Molaro (lupus@ximian.com) * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson * * (C) 2003 Ximian, Inc. */ @@ -14,11 +15,23 @@ #include #include #include +#include #include "mini-x86.h" #include "inssel.h" #include "cpu-pentium.h" +static gint lmf_tls_offset = -1; + +#ifdef PLATFORM_WIN32 +/* Under windows, the default pinvoke calling convention is stdcall */ +#define CALLCONV_IS_STDCALL(call_conv) (((call_conv) == MONO_CALL_STDCALL) || ((call_conv) == MONO_CALL_DEFAULT)) +#else +#define CALLCONV_IS_STDCALL(call_conv) ((call_conv) == MONO_CALL_STDCALL) +#endif + +static gpointer mono_arch_get_lmf_addr (void); + const char* mono_arch_regname (int reg) { switch (reg) { @@ -26,8 +39,7 @@ mono_arch_regname (int reg) { 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"; } @@ -102,13 +114,19 @@ arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgum static int indent_level = 0; static void indent (int diff) { - int v = indent_level; + int v; + if (diff < 0) + indent_level += diff; + v = indent_level; while (v-- > 0) { printf (". "); } - indent_level += diff; + if (diff > 0) + indent_level += diff; } +static gboolean enable_trace = TRUE; + static void enter_method (MonoMethod *method, char *ebp) { @@ -119,6 +137,9 @@ enter_method (MonoMethod *method, char *ebp) MonoMethodSignature *sig; char *fname; + if (!enable_trace) + return; + fname = mono_method_full_name (method, TRUE); indent (1); printf ("ENTER: %s(", fname); @@ -153,7 +174,7 @@ enter_method (MonoMethod *method, char *ebp) if (class == mono_defaults.string_class) { printf ("this:[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o)); } else { - printf ("this:%p[%s.%s], ", o, class->name_space, class->name); + printf ("this:%p[%s.%s %s], ", o, class->name_space, class->name, o->vtable->domain->friendly_name); } } else printf ("this:NULL, "); @@ -247,6 +268,9 @@ leave_method (MonoMethod *method, ...) char *fname; va_list ap; + if (!enable_trace) + return; + va_start(ap, method); fname = mono_method_full_name (method, TRUE); @@ -503,10 +527,12 @@ mono_arch_get_allocatable_int_vars (MonoCompile *cfg) (ins->inst_vtype->type == MONO_TYPE_I2) || (ins->inst_vtype->type == MONO_TYPE_CHAR)) { g_assert (MONO_VARINFO (cfg, i)->reg == -1); g_assert (i == vmv->idx); - vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE); + vars = g_list_prepend (vars, vmv); } } + vars = mono_varlist_sort (cfg, vars, 0); + return vars; } @@ -538,7 +564,7 @@ mono_arch_allocate_vars (MonoCompile *m) header = ((MonoMethodNormal *)m->method)->header; sig = m->method->signature; - + offset = 8; curinst = 0; if (MONO_TYPE_ISSTRUCT (sig->ret)) { @@ -648,23 +674,35 @@ mono_arch_allocate_vars (MonoCompile *m) */ MonoCallInst* mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) { - MonoInst *arg, *in, **rev_args; + MonoInst *arg, *in; MonoMethodSignature *sig; int i, n, stack_size, type; MonoType *ptype; + stack_size = 0; + /* add the vararg cookie before the non-implicit args */ + if (call->signature->call_convention == MONO_CALL_VARARG) { + MonoInst *sig_arg; + /* FIXME: Add support for signature tokens to AOT */ + cfg->disable_aot = TRUE; + MONO_INST_NEW (cfg, arg, OP_OUTARG); + MONO_INST_NEW (cfg, sig_arg, OP_ICONST); + sig_arg->inst_p0 = call->signature; + arg->inst_left = sig_arg; + arg->type = STACK_PTR; + /* prepend, so they get reversed */ + arg->next = call->out_args; + call->out_args = arg; + stack_size += sizeof (gpointer); + } sig = call->signature; n = sig->param_count + sig->hasthis; - rev_args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n); if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) - stack_size = 4; - else - stack_size = 0; + stack_size += sizeof (gpointer); for (i = 0; i < n; ++i) { if (is_virtual && i == 0) { /* the argument will be attached to the call instrucion */ - rev_args [n - 1] = arg = NULL; in = call->args [i]; stack_size += 4; } else { @@ -673,7 +711,9 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, arg->cil_code = in->cil_code; arg->inst_left = in; arg->type = in->type; - rev_args [n - i - 1] = arg; + /* prepend, so they get reversed */ + arg->next = call->out_args; + call->out_args = arg; if (i >= sig->hasthis) { ptype = sig->params [i - sig->hasthis]; if (ptype->byref) @@ -739,6 +779,10 @@ handle_enum: arg->unused = sig->pinvoke; arg->inst_imm = sizeof (MonoTypedRef); break; + case MONO_TYPE_GENERICINST: + type = ptype->data.generic_inst->generic_type->type; + goto handle_enum; + default: g_error ("unknown type 0x%02x in mono_arch_call_opcode\n", type); } @@ -748,11 +792,9 @@ handle_enum: } } } - /* they need to be pushed in reverse order */ /* if the function returns a struct, the called method already does a ret $0x4 */ if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) stack_size -= 4; - call->args = rev_args; call->stack_usage = stack_size; /* * should set more info in call, such as the stack space @@ -1118,6 +1160,104 @@ branch_cc_table [] = { #define DEBUG(a) if (cfg->verbose_level > 1) a //#define DEBUG(a) + +/* + * returns the offset used by spillvar. It allocates a new + * spill variable if necessary. + */ +static int +mono_spillvar_offset (MonoCompile *cfg, int spillvar) +{ + MonoSpillInfo **si, *info; + int i = 0; + + si = &cfg->spill_info; + + while (i <= spillvar) { + + if (!*si) { + *si = info = mono_mempool_alloc (cfg->mempool, sizeof (MonoSpillInfo)); + info->next = NULL; + cfg->stack_offset -= sizeof (gpointer); + info->offset = cfg->stack_offset; + } + + if (i == spillvar) + return (*si)->offset; + + i++; + si = &(*si)->next; + } + + g_assert_not_reached (); + return 0; +} + +/* + * returns the offset used by spillvar. It allocates a new + * spill float variable if necessary. + * (same as mono_spillvar_offset but for float) + */ +static int +mono_spillvar_offset_float (MonoCompile *cfg, int spillvar) +{ + MonoSpillInfo **si, *info; + int i = 0; + + si = &cfg->spill_info_float; + + while (i <= spillvar) { + + if (!*si) { + *si = info = mono_mempool_alloc (cfg->mempool, sizeof (MonoSpillInfo)); + info->next = NULL; + cfg->stack_offset -= sizeof (double); + info->offset = cfg->stack_offset; + } + + if (i == spillvar) + return (*si)->offset; + + i++; + si = &(*si)->next; + } + + g_assert_not_reached (); + return 0; +} + +/* + * Creates a store for spilled floating point items + */ +static MonoInst* +create_spilled_store_float (MonoCompile *cfg, int spill, int reg, MonoInst *ins) +{ + MonoInst *store; + MONO_INST_NEW (cfg, store, OP_STORER8_MEMBASE_REG); + store->sreg1 = reg; + store->inst_destbasereg = X86_EBP; + store->inst_offset = mono_spillvar_offset_float (cfg, spill); + + DEBUG (g_print ("SPILLED FLOAT STORE (%d at 0x%08x(%%sp)) (from %d)\n", spill, store->inst_offset, reg)); + return store; +} + +/* + * Creates a load for spilled floating point items + */ +static MonoInst* +create_spilled_load_float (MonoCompile *cfg, int spill, int reg, MonoInst *ins) +{ + MonoInst *load; + MONO_INST_NEW (cfg, load, OP_LOADR8_SPILL_MEMBASE); + load->dreg = reg; + load->inst_basereg = X86_EBP; + load->inst_offset = mono_spillvar_offset_float (cfg, spill); + + DEBUG (g_print ("SPILLED FLOAT LOAD (%d at 0x%08x(%%sp)) (from %d)\n", spill, load->inst_offset, reg)); + return load; +} + #define reg_is_freeable(r) ((r) >= 0 && (r) <= 7 && X86_IS_CALLEE ((r))) typedef struct { @@ -1125,6 +1265,7 @@ typedef struct { int killed_in; int last_use; int prev_use; + int flags; /* used to track fp spill/load */ } RegTrack; static const char*const * ins_spec = pentium_desc; @@ -1342,6 +1483,7 @@ insert_before_ins (MonoInst *ins, InstList *item, MonoInst* to_insert) item->data = to_insert; } + #if 0 static int alloc_int_reg (MonoCompile *cfg, InstList *curinst, MonoInst *ins, int sym_reg, guint32 allow_mask) @@ -1366,7 +1508,12 @@ alloc_int_reg (MonoCompile *cfg, InstList *curinst, MonoInst *ins, int sym_reg, } #endif -#include "cprop.c" +/* flags used in reginfo->flags */ +#define MONO_X86_FP_NEEDS_LOAD_SPILL 1 +#define MONO_X86_FP_NEEDS_SPILL 2 +#define MONO_X86_FP_NEEDS_LOAD 4 + +/*#include "cprop.c"*/ /* * Local register allocation. @@ -1386,31 +1533,43 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) InstList *tmp, *reversed = NULL; const char *spec; guint32 src1_mask, src2_mask, dest_mask; + GList *fspill_list = NULL; + int fspill = 0; if (!bb->code) return; rs->next_vireg = bb->max_ireg; rs->next_vfreg = bb->max_freg; mono_regstate_assign (rs); - reginfo = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vireg); - reginfof = mono_mempool_alloc0 (cfg->mempool, sizeof (RegTrack) * rs->next_vfreg); + reginfo = g_malloc0 (sizeof (RegTrack) * rs->next_vireg); + reginfof = g_malloc0 (sizeof (RegTrack) * rs->next_vfreg); rs->ifree_mask = X86_CALLEE_REGS; ins = bb->code; - if (cfg->opt & MONO_OPT_COPYPROP) - local_copy_prop (cfg, ins); - + /*if (cfg->opt & MONO_OPT_COPYPROP) + local_copy_prop (cfg, ins);*/ + i = 1; - fpcount = 0; /* FIXME: track fp stack utilization */ + fpcount = 0; DEBUG (g_print ("LOCAL regalloc: basic block: %d\n", bb->block_num)); /* forward pass on the instructions to collect register liveness info */ while (ins) { spec = ins_spec [ins->opcode]; DEBUG (print_ins (i, ins)); + if (spec [MONO_INST_SRC1]) { - if (spec [MONO_INST_SRC1] == 'f') + if (spec [MONO_INST_SRC1] == 'f') { + GList *spill; reginfo1 = reginfof; + + spill = g_list_first (fspill_list); + if (spill && fpcount < MONO_MAX_FREGS) { + reginfo1 [ins->sreg1].flags |= MONO_X86_FP_NEEDS_LOAD; + fspill_list = g_list_remove (fspill_list, spill->data); + } else + fpcount--; + } else reginfo1 = reginfo; reginfo1 [ins->sreg1].prev_use = reginfo1 [ins->sreg1].last_use; @@ -1419,8 +1578,21 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) ins->sreg1 = -1; } if (spec [MONO_INST_SRC2]) { - if (spec [MONO_INST_SRC2] == 'f') + if (spec [MONO_INST_SRC2] == 'f') { + GList *spill; reginfo2 = reginfof; + spill = g_list_first (fspill_list); + if (spill) { + reginfo2 [ins->sreg2].flags |= MONO_X86_FP_NEEDS_LOAD; + fspill_list = g_list_remove (fspill_list, spill->data); + if (fpcount >= MONO_MAX_FREGS) { + fspill++; + fspill_list = g_list_prepend (fspill_list, GINT_TO_POINTER(fspill)); + reginfo2 [ins->sreg2].flags |= MONO_X86_FP_NEEDS_LOAD_SPILL; + } + } else + fpcount--; + } else reginfo2 = reginfo; reginfo2 [ins->sreg2].prev_use = reginfo2 [ins->sreg2].last_use; @@ -1429,8 +1601,16 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) ins->sreg2 = -1; } if (spec [MONO_INST_DEST]) { - if (spec [MONO_INST_DEST] == 'f') + if (spec [MONO_INST_DEST] == 'f') { reginfod = reginfof; + if (fpcount >= MONO_MAX_FREGS) { + reginfod [ins->dreg].flags |= MONO_X86_FP_NEEDS_SPILL; + fspill++; + fspill_list = g_list_prepend (fspill_list, GINT_TO_POINTER(fspill)); + fpcount--; + } + fpcount++; + } else reginfod = reginfo; if (spec [MONO_INST_DEST] != 'b') /* it's not just a base register */ @@ -1445,7 +1625,7 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) reginfod [ins->dreg + 1].last_use = i; if (reginfod [ins->dreg + 1].born_in == 0 || reginfod [ins->dreg + 1].born_in > i) reginfod [ins->dreg + 1].born_in = i; - } + } } else { ins->dreg = -1; } @@ -1454,6 +1634,9 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) ins = ins->next; } + // todo: check if we have anything left on fp stack, in verify mode? + fspill = 0; + DEBUG (print_regtrack (reginfo, rs->next_vireg)); DEBUG (print_regtrack (reginfof, rs->next_vfreg)); tmp = reversed; @@ -1463,6 +1646,7 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) --i; ins = tmp->data; spec = ins_spec [ins->opcode]; + prev_dreg = -1; DEBUG (g_print ("processing:")); DEBUG (print_ins (i, ins)); if (spec [MONO_INST_CLOB] == 's') { @@ -1566,6 +1750,7 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) } } else { DEBUG (g_print ("\tshortcut assignment of R%d to %s\n", ins->dreg, mono_arch_regname (dest_reg))); + prev_dreg = ins->dreg; rs->iassign [ins->dreg] = dest_reg; rs->isymbolic [dest_reg] = ins->dreg; ins->dreg = dest_reg; @@ -1597,8 +1782,22 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) mono_regstate_free_int (rs, X86_EDX); } } - /* update for use with FP regs... */ - if (spec [MONO_INST_DEST] != 'f' && ins->dreg >= MONO_MAX_IREGS) { + + /* Track dreg */ + if (spec [MONO_INST_DEST] == 'f') { + if (reginfof [ins->dreg].flags & MONO_X86_FP_NEEDS_SPILL) { + GList *spill_node; + MonoInst *store; + spill_node = g_list_first (fspill_list); + g_assert (spill_node); + + store = create_spilled_store_float (cfg, GPOINTER_TO_INT (spill_node->data), ins->dreg, ins); + insert_before_ins (ins, tmp, store); + fspill_list = g_list_remove (fspill_list, spill_node->data); + fspill--; + } + } + else if (ins->dreg >= MONO_MAX_IREGS) { val = rs->iassign [ins->dreg]; prev_dreg = ins->dreg; if (val < 0) { @@ -1667,8 +1866,6 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) } else if (spec [MONO_INST_DEST] == 'd' && ins->dreg != X86_EDX && spec [MONO_INST_CLOB] != 'd') { create_copy_ins (cfg, ins->dreg, X86_EDX, ins); } - } else { - prev_dreg = -1; } if (spec [MONO_INST_DEST] != 'f' && reg_is_freeable (ins->dreg) && prev_dreg >= 0 && reginfo [prev_dreg].born_in >= i) { DEBUG (g_print ("\tfreeable %s (R%d) (born in %d)\n", mono_arch_regname (ins->dreg), prev_dreg, reginfo [prev_dreg].born_in)); @@ -1687,7 +1884,31 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) ins->sreg1 = X86_EAX; rs->ifree_mask &= ~ (1 << X86_EAX); } - if (spec [MONO_INST_SRC1] != 'f' && ins->sreg1 >= MONO_MAX_IREGS) { + + /* Track sreg1 */ + if (spec [MONO_INST_SRC1] == 'f') { + if (reginfof [ins->sreg1].flags & MONO_X86_FP_NEEDS_LOAD) { + MonoInst *load; + MonoInst *store = NULL; + + if (reginfof [ins->sreg1].flags & MONO_X86_FP_NEEDS_LOAD_SPILL) { + GList *spill_node; + spill_node = g_list_first (fspill_list); + g_assert (spill_node); + + store = create_spilled_store_float (cfg, GPOINTER_TO_INT (spill_node->data), ins->sreg1, ins); + fspill_list = g_list_remove (fspill_list, spill_node->data); + } + + fspill++; + fspill_list = g_list_prepend (fspill_list, GINT_TO_POINTER(fspill)); + load = create_spilled_load_float (cfg, fspill, ins->sreg1, ins); + insert_before_ins (ins, tmp, load); + if (store) + insert_before_ins (load, tmp, store); + } + } + else if (ins->sreg1 >= MONO_MAX_IREGS) { val = rs->iassign [ins->sreg1]; prev_sreg1 = ins->sreg1; if (val < 0) { @@ -1742,7 +1963,33 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) ins->next = copy; } } - if (spec [MONO_INST_SRC2] != 'f' && ins->sreg2 >= MONO_MAX_IREGS) { + /* track sreg2 */ + if (spec [MONO_INST_SRC2] == 'f') { + if (reginfof [ins->sreg2].flags & MONO_X86_FP_NEEDS_LOAD) { + MonoInst *load; + MonoInst *store = NULL; + + if (reginfof [ins->sreg2].flags & MONO_X86_FP_NEEDS_LOAD_SPILL) { + GList *spill_node; + + spill_node = g_list_first (fspill_list); + g_assert (spill_node); + if (spec [MONO_INST_SRC1] == 'f' && (reginfof [ins->sreg1].flags & MONO_X86_FP_NEEDS_LOAD_SPILL)) + spill_node = g_list_next (spill_node); + + store = create_spilled_store_float (cfg, GPOINTER_TO_INT (spill_node->data), ins->sreg2, ins); + fspill_list = g_list_remove (fspill_list, spill_node->data); + } + + fspill++; + fspill_list = g_list_prepend (fspill_list, GINT_TO_POINTER(fspill)); + load = create_spilled_load_float (cfg, fspill, ins->sreg2, ins); + insert_before_ins (ins, tmp, load); + if (store) + insert_before_ins (load, tmp, store); + } + } + else if (ins->sreg2 >= MONO_MAX_IREGS) { val = rs->iassign [ins->sreg2]; prev_sreg2 = ins->sreg2; if (val < 0) { @@ -1786,13 +2033,17 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb) DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg2))); mono_regstate_free_int (rs, ins->sreg2); }*/ - + //DEBUG (print_ins (i, ins)); /* this may result from a insert_before call */ if (!tmp->next) bb->code = tmp->data; tmp = tmp->next; } + + g_free (reginfo); + g_free (reginfof); + g_list_free (fspill_list); } static unsigned char* @@ -2315,18 +2566,13 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_VOIDCALL: case CEE_CALL: call = (MonoCallInst*)ins; - if (call->signature->call_convention == MONO_CALL_VARARG) { - /* FIXME: hack */ - x86_push_imm (code, call->signature); - offset = code - cfg->native_code; - } if (ins->flags & MONO_INST_HAS_METHOD) mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method); else { mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr); } x86_call_code (code, 0); - if (call->stack_usage && (call->signature->call_convention != MONO_CALL_STDCALL)) + if (call->stack_usage && !CALLCONV_IS_STDCALL (call->signature->call_convention)) x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage); break; case OP_FCALL_REG: @@ -2336,7 +2582,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_CALL_REG: call = (MonoCallInst*)ins; x86_call_reg (code, ins->sreg1); - if (call->stack_usage && (call->signature->call_convention != MONO_CALL_STDCALL)) + if (call->stack_usage && !CALLCONV_IS_STDCALL (call->signature->call_convention)) x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage); break; case OP_FCALL_MEMBASE: @@ -2346,7 +2592,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_CALL_MEMBASE: call = (MonoCallInst*)ins; x86_call_membase (code, ins->sreg1, ins->inst_offset); - if (call->stack_usage && (call->signature->call_convention != MONO_CALL_STDCALL)) + if (call->stack_usage && !CALLCONV_IS_STDCALL (call->signature->call_convention)) x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage); break; case OP_OUTARG: @@ -2491,7 +2737,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_R8CONST: { double d = *(double *)ins->inst_p0; - if ((d == 0.0) && (signbit (d) == 0)) { + if ((d == 0.0) && (mono_signbit (d) == 0)) { x86_fldz (code); } else if (d == 1.0) { x86_fld1 (code); @@ -2504,7 +2750,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_R4CONST: { float f = *(float *)ins->inst_p0; - if ((f == 0.0) && (signbit (f) == 0)) { + if ((f == 0.0) && (mono_signbit (f) == 0)) { x86_fldz (code); } else if (f == 1.0) { x86_fld1 (code); @@ -2517,6 +2763,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_STORER8_MEMBASE_REG: x86_fst_membase (code, ins->inst_destbasereg, ins->inst_offset, TRUE, TRUE); break; + case OP_LOADR8_SPILL_MEMBASE: + x86_fld_membase (code, ins->inst_basereg, ins->inst_offset, TRUE); + x86_fxch (code, 1); + break; case OP_LOADR8_MEMBASE: x86_fld_membase (code, ins->inst_basereg, ins->inst_offset, TRUE); break; @@ -2966,12 +3216,13 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) void mono_arch_register_lowlevel_calls (void) { + mono_register_jit_icall (mono_arch_get_lmf_addr, "mono_arch_get_lmf_addr", NULL, TRUE); mono_register_jit_icall (enter_method, "mono_enter_method", NULL, TRUE); mono_register_jit_icall (leave_method, "mono_leave_method", NULL, TRUE); } void -mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji) +mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors) { MonoJumpInfo *patch_info; @@ -2992,6 +3243,9 @@ mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, Mono case MONO_PATCH_INFO_IP: *((gpointer *)(ip)) = ip; continue; + case MONO_PATCH_INFO_METHOD_REL: + *((gpointer *)(ip)) = code + patch_info->data.offset; + continue; case MONO_PATCH_INFO_INTERNAL_METHOD: { MonoJitICallInfo *mi = mono_find_jit_icall_by_name (patch_info->data.name); if (!mi) { @@ -3001,37 +3255,35 @@ mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, Mono target = mono_icall_get_wrapper (mi); break; } - case MONO_PATCH_INFO_METHOD_JUMP: + case MONO_PATCH_INFO_METHOD_JUMP: { + GSList *list; + /* get the trampoline to the method from the domain */ - if (!(target = g_hash_table_lookup (domain->jit_code_hash, patch_info->data.method))) { - GSList *list; - target = mono_arch_create_jump_trampoline (patch_info->data.method); - if (!domain->jump_target_hash) - domain->jump_target_hash = g_hash_table_new (NULL, NULL); - list = g_hash_table_lookup (domain->jump_target_hash, patch_info->data.method); - list = g_slist_prepend (list, ip); - g_hash_table_insert (domain->jump_target_hash, patch_info->data.method, list); - } + target = mono_create_jump_trampoline (domain, patch_info->data.method, TRUE); + if (!domain->jump_target_hash) + domain->jump_target_hash = g_hash_table_new (NULL, NULL); + list = g_hash_table_lookup (domain->jump_target_hash, patch_info->data.method); + list = g_slist_prepend (list, ip); + g_hash_table_insert (domain->jump_target_hash, patch_info->data.method, list); break; + } case MONO_PATCH_INFO_METHOD: if (patch_info->data.method == method) { target = code; - } else { + } else /* get the trampoline to the method from the domain */ - if (!(target = g_hash_table_lookup (domain->jit_code_hash, patch_info->data.method))) - target = mono_arch_create_jit_trampoline (patch_info->data.method); - } + target = mono_arch_create_jit_trampoline (patch_info->data.method); break; case MONO_PATCH_INFO_SWITCH: { - gpointer *table = (gpointer *)patch_info->data.target; + gpointer *jump_table = mono_mempool_alloc (domain->code_mp, sizeof (gpointer) * patch_info->table_size); int i; - *((gconstpointer *)(ip + 2)) = patch_info->data.target; + *((gconstpointer *)(ip + 2)) = jump_table; for (i = 0; i < patch_info->table_size; i++) { - table [i] = (int)patch_info->data.table [i] + code; + jump_table [i] = code + (int)patch_info->data.table [i]; } - /* we put into the table the absolute address, no need fo x86_patch in this case */ + /* we put into the table the absolute address, no need for x86_patch in this case */ continue; } case MONO_PATCH_INFO_METHODCONST: @@ -3040,10 +3292,69 @@ mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, Mono case MONO_PATCH_INFO_FIELD: *((gconstpointer *)(ip + 1)) = patch_info->data.target; continue; + case MONO_PATCH_INFO_IID: + mono_class_init (patch_info->data.klass); + *((guint32 *)(ip + 1)) = patch_info->data.klass->interface_id; + continue; + case MONO_PATCH_INFO_VTABLE: + *((gconstpointer *)(ip + 1)) = mono_class_vtable (domain, patch_info->data.klass); + continue; + case MONO_PATCH_INFO_CLASS_INIT: { + guint8 *code = ip; + /* Might already been changed to a nop */ + x86_call_imm (code, 0); + target = mono_create_class_init_trampoline (mono_class_vtable (domain, patch_info->data.klass)); + break; + } + case MONO_PATCH_INFO_SFLDA: { + MonoVTable *vtable = mono_class_vtable (domain, patch_info->data.field->parent); + if (!vtable->initialized && !(vtable->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) && mono_class_needs_cctor_run (vtable->klass, method)) + /* Done by the generated code */ + ; + else { + if (run_cctors) + mono_runtime_class_init (vtable); + } + *((gconstpointer *)(ip + 1)) = + (char*)vtable->data + patch_info->data.field->offset; + continue; + } case MONO_PATCH_INFO_R4: case MONO_PATCH_INFO_R8: *((gconstpointer *)(ip + 2)) = patch_info->data.target; continue; + case MONO_PATCH_INFO_EXC_NAME: + *((gconstpointer *)(ip + 1)) = patch_info->data.name; + continue; + case MONO_PATCH_INFO_LDSTR: + *((gconstpointer *)(ip + 1)) = + mono_ldstr (domain, patch_info->data.token->image, + mono_metadata_token_index (patch_info->data.token->token)); + continue; + case MONO_PATCH_INFO_TYPE_FROM_HANDLE: { + gpointer handle; + MonoClass *handle_class; + + handle = mono_ldtoken (patch_info->data.token->image, + patch_info->data.token->token, &handle_class); + mono_class_init (handle_class); + mono_class_init (mono_class_from_mono_type (handle)); + + *((gconstpointer *)(ip + 1)) = + mono_type_get_object (domain, handle); + continue; + } + case MONO_PATCH_INFO_LDTOKEN: { + gpointer handle; + MonoClass *handle_class; + + handle = mono_ldtoken (patch_info->data.token->image, + patch_info->data.token->token, &handle_class); + mono_class_init (handle_class); + + *((gconstpointer *)(ip + 1)) = handle; + continue; + } default: g_assert_not_reached (); } @@ -3060,7 +3371,7 @@ mono_arch_max_epilog_size (MonoCompile *cfg) if (cfg->method->save_lmf) max_epilog_size += 128; - if (mono_jit_trace_calls) + if (mono_jit_trace_calls != NULL) max_epilog_size += 50; if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) @@ -3116,11 +3427,28 @@ mono_arch_emit_prolog (MonoCompile *cfg) /* save method info */ x86_push_imm (code, method); - + /* get the address of lmf for the current thread */ - mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, - (gpointer)"mono_get_lmf_addr"); - x86_call_code (code, 0); + /* + * 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 */ + x86_prefix (code, X86_GS_PREFIX); + x86_mov_reg_mem (code, X86_EAX, 0, 4); + x86_mov_reg_membase (code, X86_EAX, X86_EAX, lmf_tls_offset, 4); + } + else { +#ifdef HAVE_KW_THREAD + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, + (gpointer)"mono_arch_get_lmf_addr"); +#else + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, + (gpointer)"mono_get_lmf_addr"); +#endif + x86_call_code (code, 0); + } /* push lmf */ x86_push_reg (code, X86_EAX); @@ -3148,8 +3476,21 @@ mono_arch_emit_prolog (MonoCompile *cfg) alloc_size -= pos; - if (alloc_size) + if (alloc_size) { + /* See mono_emit_stack_alloc */ +#ifdef PLATFORM_WIN32 + guint32 remaining_size = alloc_size; + while (remaining_size >= 0x1000) { + x86_alu_reg_imm (code, X86_SUB, X86_ESP, 0x1000); + x86_test_membase_reg (code, X86_ESP, 0, X86_ESP); + remaining_size -= 0x1000; + } + if (remaining_size) + x86_alu_reg_imm (code, X86_SUB, X86_ESP, remaining_size); +#else x86_alu_reg_imm (code, X86_SUB, X86_ESP, alloc_size); +#endif + } /* compute max_offset in order to use short forward jumps */ max_offset = 0; @@ -3168,7 +3509,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) } } - if (mono_jit_trace_calls) + if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) code = mono_arch_instrument_prolog (cfg, enter_method, code, TRUE); /* load arguments allocated to register from the stack */ @@ -3195,12 +3536,14 @@ mono_arch_emit_epilog (MonoCompile *cfg) { MonoJumpInfo *patch_info; MonoMethod *method = cfg->method; + MonoMethodSignature *sig = method->signature; int pos; + guint32 stack_to_pop; guint8 *code; code = cfg->native_code + cfg->code_len; - if (mono_jit_trace_calls) + if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) code = mono_arch_instrument_epilog (cfg, leave_method, code, TRUE); /* the code restoring the registers must be kept in sync with CEE_JMP */ @@ -3254,9 +3597,20 @@ mono_arch_emit_epilog (MonoCompile *cfg) } x86_leave (code); - /* FIXME: add another check to support stdcall convention here */ + + if (CALLCONV_IS_STDCALL (sig->call_convention)) { + MonoJitArgumentInfo *arg_info = alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1)); + + stack_to_pop = arch_get_argument_info (sig, sig->param_count, arg_info); + } + else if (MONO_TYPE_ISSTRUCT (cfg->method->signature->ret)) - x86_ret_imm (code, 4); + stack_to_pop = 4; + else + stack_to_pop = 0; + + if (stack_to_pop) + x86_ret_imm (code, stack_to_pop); else x86_ret (code); @@ -3265,7 +3619,9 @@ mono_arch_emit_epilog (MonoCompile *cfg) switch (patch_info->type) { case MONO_PATCH_INFO_EXC: x86_patch (patch_info->ip.i + cfg->native_code, code); + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC_NAME, patch_info->data.target); x86_push_imm (code, patch_info->data.target); + mono_add_patch_info (cfg, code + 1 - cfg->native_code, MONO_PATCH_INFO_METHOD_REL, (gpointer)patch_info->ip.i); x86_push_imm (code, patch_info->ip.i + cfg->native_code); patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD; patch_info->data.name = "mono_arch_throw_exception_by_name"; @@ -3290,3 +3646,108 @@ mono_arch_flush_icache (guint8 *code, gint size) /* not needed */ } +/* + * Support for fast access to the thread-local lmf structure using the GS + * segment register on NPTL + kernel 2.6.x. + */ + +static gboolean tls_offset_inited = FALSE; + +#ifdef HAVE_KW_THREAD +static __thread gpointer mono_lmf_addr; +#endif + +static gpointer +mono_arch_get_lmf_addr (void) +{ +#ifdef HAVE_KW_THREAD + return mono_lmf_addr; +#else + g_assert_not_reached (); + return NULL; +#endif +} + +void +mono_arch_setup_jit_tls_data (MonoJitTlsData *tls) +{ + if (!tls_offset_inited) { + guint8 *code; + + tls_offset_inited = TRUE; + + if (getenv ("MONO_NPTL")) { + /* + * Determine the offset of mono_lfm_addr inside the TLS structures + * by disassembling the function above. + */ + code = (guint8*)&mono_arch_get_lmf_addr; + + /* This is generated by gcc 3.3.2 */ + if ((code [0] == 0x55) && (code [1] == 0x89) && (code [2] == 0xe5) && + (code [3] == 0x65) && (code [4] == 0xa1) && (code [5] == 0x00) && + (code [6] == 0x00) && (code [7] == 0x00) && (code [8] == 0x00) && + (code [9] == 0x8b) && (code [10] == 0x80)) { + lmf_tls_offset = *(int*)&(code [11]); + } + } + } + +#ifdef HAVE_KW_THREAD + mono_lmf_addr = &tls->lmf; +#endif +} + +void +mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg) +{ + + /* add the this argument */ + if (this_reg != -1) { + MonoInst *this; + MONO_INST_NEW (cfg, this, OP_OUTARG); + this->type = this_type; + this->sreg1 = this_reg; + mono_bblock_add_inst (cfg->cbb, this); + } + + if (vt_reg != -1) { + MonoInst *vtarg; + MONO_INST_NEW (cfg, vtarg, OP_OUTARG); + vtarg->type = STACK_MP; + vtarg->sreg1 = vt_reg; + mono_bblock_add_inst (cfg->cbb, vtarg); + } +} + + +gint +mono_arch_get_opcode_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +{ + if (cmethod->klass == mono_defaults.math_class) { + if (strcmp (cmethod->name, "Sin") == 0) + return OP_SIN; + else if (strcmp (cmethod->name, "Cos") == 0) + return OP_COS; + else if (strcmp (cmethod->name, "Tan") == 0) + return OP_TAN; + else if (strcmp (cmethod->name, "Atan") == 0) + return OP_ATAN; + else if (strcmp (cmethod->name, "Sqrt") == 0) + return OP_SQRT; + else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) + return OP_ABS; +#if 0 + /* OP_FREM is not IEEE compatible */ + else if (strcmp (cmethod->name, "IEEERemainder") == 0) + return OP_FREM; +#endif + else + return -1; + } else { + return -1; + } + return -1; +} + +