X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini-x86.c;h=a7abb8871295253d2edf10a64cafa3fd6f66739d;hb=92f60fbffa1187be7dedf6a9286b5c35b2082815;hp=1ee4ae4940d00ae43791bba239dc58318592eed9;hpb=adba453b57e3a28c37dcc56b0103fc54f717fb81;p=mono.git diff --git a/mono/mini/mini-x86.c b/mono/mini/mini-x86.c index 1ee4ae4940d..a7abb887129 100644 --- a/mono/mini/mini-x86.c +++ b/mono/mini/mini-x86.c @@ -20,8 +20,10 @@ #include #include #include +#include #include #include +#include #include "trace.h" #include "mini-x86.h" @@ -32,7 +34,6 @@ 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 static gboolean optimize_for_xen = TRUE; @@ -40,7 +41,7 @@ static gboolean optimize_for_xen = TRUE; #define optimize_for_xen 0 #endif -#ifdef PLATFORM_WIN32 +#ifdef TARGET_WIN32 static gboolean is_win32 = TRUE; #else static gboolean is_win32 = FALSE; @@ -55,7 +56,7 @@ static CRITICAL_SECTION mini_arch_mutex; #define ARGS_OFFSET 8 -#ifdef PLATFORM_WIN32 +#ifdef TARGET_WIN32 /* Under windows, the default pinvoke calling convention is stdcall */ #define CALLCONV_IS_STDCALL(sig) ((((sig)->call_convention) == MONO_CALL_STDCALL) || ((sig)->pinvoke && ((sig)->call_convention) == MONO_CALL_DEFAULT)) #else @@ -65,6 +66,74 @@ static CRITICAL_SECTION mini_arch_mutex; MonoBreakpointInfo mono_breakpoint_info [MONO_BREAKPOINT_ARRAY_SIZE]; +static gpointer +mono_realloc_native_code (MonoCompile *cfg) +{ +#ifdef __native_client_codegen__ + guint old_padding; + gpointer native_code; + guint alignment_check; + + /* Save the old alignment offset so we can re-align after the realloc. */ + old_padding = (guint)(cfg->native_code - cfg->native_code_alloc); + + cfg->native_code_alloc = g_realloc (cfg->native_code_alloc, + cfg->code_size + kNaClAlignment); + + /* Align native_code to next nearest kNaClAlignment byte. */ + native_code = (guint)cfg->native_code_alloc + kNaClAlignment; + native_code = (guint)native_code & ~kNaClAlignmentMask; + + /* Shift the data to be 32-byte aligned again. */ + memmove (native_code, cfg->native_code_alloc + old_padding, cfg->code_size); + + alignment_check = (guint)native_code & kNaClAlignmentMask; + g_assert (alignment_check == 0); + return native_code; +#else + return g_realloc (cfg->native_code, cfg->code_size); +#endif +} + +#ifdef __native_client_codegen__ + +/* mono_arch_nacl_pad: Add pad bytes of alignment instructions at code, */ +/* Check that alignment doesn't cross an alignment boundary. */ +guint8 * +mono_arch_nacl_pad (guint8 *code, int pad) +{ + const int kMaxPadding = 7; /* see x86-codegen.h: x86_padding() */ + + if (pad == 0) return code; + /* assertion: alignment cannot cross a block boundary */ + g_assert(((uintptr_t)code & (~kNaClAlignmentMask)) == + (((uintptr_t)code + pad - 1) & (~kNaClAlignmentMask))); + while (pad >= kMaxPadding) { + x86_padding (code, kMaxPadding); + pad -= kMaxPadding; + } + if (pad != 0) x86_padding (code, pad); + return code; +} + +guint8 * +mono_arch_nacl_skip_nops (guint8 *code) +{ + x86_skip_nops (code); + return code; +} + +#endif /* __native_client_codegen__ */ + +/* + * The code generated for sequence points reads from this location, which is + * made read-only when single stepping is enabled. + */ +static gpointer ss_trigger_page; + +/* Enabled breakpoints read from this trigger page */ +static gpointer bp_trigger_page; + const char* mono_arch_regname (int reg) { @@ -131,6 +200,11 @@ mono_arch_xregname (int reg) } } +void +mono_x86_patch (unsigned char* code, gpointer target) +{ + x86_patch (code, (unsigned char*)target); +} typedef enum { ArgInIReg, @@ -160,6 +234,9 @@ typedef struct { guint32 freg_usage; gboolean need_stack_align; guint32 stack_align_amount; + gboolean vtype_retaddr; + /* The index of the vret arg in the argument list */ + int vret_arg_index; ArgInfo ret; ArgInfo sig_cookie; ArgInfo args [1]; @@ -171,7 +248,7 @@ typedef struct { static X86_Reg_No param_regs [] = { 0 }; -#if defined(PLATFORM_WIN32) || defined(__APPLE__) || defined(__FreeBSD__) +#if defined(TARGET_WIN32) || defined(__APPLE__) || defined(__FreeBSD__) #define SMALL_STRUCTS_IN_REGS static X86_Reg_No return_regs [] = { X86_EAX, X86_EDX }; #endif @@ -287,18 +364,12 @@ add_valuetype (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, ArgIn * For x86 win32, see ???. */ static CallInfo* -get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) +get_call_info_internal (MonoGenericSharingContext *gsctx, CallInfo *cinfo, MonoMethodSignature *sig, gboolean is_pinvoke) { - guint32 i, gr, fr; + guint32 i, gr, fr, pstart; MonoType *ret_type; int n = sig->hasthis + sig->param_count; guint32 stack_size = 0; - CallInfo *cinfo; - - 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; @@ -339,7 +410,7 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign cinfo->ret.storage = ArgOnDoubleFpStack; break; case MONO_TYPE_GENERICINST: - if (!mono_type_generic_inst_is_valuetype (sig->ret)) { + if (!mono_type_generic_inst_is_valuetype (ret_type)) { cinfo->ret.storage = ArgInIReg; cinfo->ret.reg = X86_EAX; break; @@ -349,15 +420,15 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign guint32 tmp_gr = 0, tmp_fr = 0, tmp_stacksize = 0; add_valuetype (gsctx, sig, &cinfo->ret, sig->ret, TRUE, &tmp_gr, &tmp_fr, &tmp_stacksize); - if (cinfo->ret.storage == ArgOnStack) + if (cinfo->ret.storage == ArgOnStack) { + cinfo->vtype_retaddr = TRUE; /* The caller passes the address where the value is stored */ - add_general (&gr, &stack_size, &cinfo->ret); + } break; } case MONO_TYPE_TYPEDBYREF: - /* Same as a valuetype with size 24 */ - add_general (&gr, &stack_size, &cinfo->ret); - ; + /* Same as a valuetype with size 12 */ + cinfo->vtype_retaddr = TRUE; break; case MONO_TYPE_VOID: cinfo->ret.storage = ArgNone; @@ -367,9 +438,31 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign } } - /* this */ - if (sig->hasthis) - add_general (&gr, &stack_size, cinfo->args + 0); + pstart = 0; + /* + * To simplify get_this_arg_reg () and LLVM integration, emit the vret arg after + * the first argument, allowing 'this' to be always passed in the first arg reg. + * Also do this if the first argument is a reference type, since virtual calls + * are sometimes made using calli without sig->hasthis set, like in the delegate + * invoke wrappers. + */ + if (cinfo->vtype_retaddr && !is_pinvoke && (sig->hasthis || (sig->param_count > 0 && MONO_TYPE_IS_REFERENCE (mini_type_get_underlying_type (gsctx, sig->params [0]))))) { + if (sig->hasthis) { + add_general (&gr, &stack_size, cinfo->args + 0); + } else { + add_general (&gr, &stack_size, &cinfo->args [sig->hasthis + 0]); + pstart = 1; + } + add_general (&gr, &stack_size, &cinfo->ret); + cinfo->vret_arg_index = 1; + } else { + /* this */ + if (sig->hasthis) + add_general (&gr, &stack_size, cinfo->args + 0); + + if (cinfo->vtype_retaddr) + add_general (&gr, &stack_size, &cinfo->ret); + } if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (n == 0)) { gr = PARAM_REGS; @@ -379,7 +472,7 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign add_general (&gr, &stack_size, &cinfo->sig_cookie); } - for (i = 0; i < sig->param_count; ++i) { + for (i = pstart; i < sig->param_count; ++i) { ArgInfo *ainfo = &cinfo->args [sig->hasthis + i]; MonoType *ptype; @@ -428,7 +521,7 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign add_general (&gr, &stack_size, ainfo); break; case MONO_TYPE_GENERICINST: - if (!mono_type_generic_inst_is_valuetype (sig->params [i])) { + if (!mono_type_generic_inst_is_valuetype (ptype)) { add_general (&gr, &stack_size, ainfo); break; } @@ -476,6 +569,20 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign return cinfo; } +static CallInfo* +get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) +{ + int n = sig->hasthis + sig->param_count; + CallInfo *cinfo; + + if (mp) + cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n)); + else + cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n)); + + return get_call_info_internal (gsctx, cinfo, sig, is_pinvoke); +} + /* * mono_arch_get_argument_info: * @csig: a method signature @@ -486,22 +593,25 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign * padding. arg_info should be large enought to hold param_count + 1 entries. * * Returns the size of the argument area on the stack. + * This should be signal safe, since it is called from + * mono_arch_find_jit_info_ext (). + * FIXME: The metadata calls might not be signal safe. */ int mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info) { - int k, args_size = 0; + int len, k, args_size = 0; int size, pad; guint32 align; int offset = 8; CallInfo *cinfo; - cinfo = get_call_info (NULL, NULL, csig, FALSE); + /* Avoid g_malloc as it is not signal safe */ + len = sizeof (CallInfo) + (sizeof (ArgInfo) * (csig->param_count + 1)); + cinfo = (CallInfo*)g_newa (guint8*, len); + memset (cinfo, 0, len); - if (MONO_TYPE_ISSTRUCT (csig->ret) && (cinfo->ret.storage == ArgOnStack)) { - args_size += sizeof (gpointer); - offset += 4; - } + cinfo = get_call_info_internal (NULL, cinfo, csig, FALSE); arg_info [0].offset = offset; @@ -510,6 +620,11 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit offset += 4; } + if (MONO_TYPE_ISSTRUCT (csig->ret) && (cinfo->ret.storage == ArgOnStack)) { + args_size += sizeof (gpointer); + offset += 4; + } + arg_info [0].size = args_size; for (k = 0; k < param_count; k++) { @@ -535,8 +650,6 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit args_size += pad = (align - (args_size & (align - 1))) & (align - 1); arg_info [k].pad = pad; - g_free (cinfo); - return args_size; } @@ -566,6 +679,14 @@ typedef void (*CpuidFunc) (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_ed static int cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx) { +#if defined(__native_client__) + /* Taken from below, the bug listed in the comment is */ + /* only valid for non-static cases. */ + __asm__ __volatile__ ("cpuid" + : "=a" (*p_eax), "=b" (*p_ebx), "=c" (*p_ecx), "=d" (*p_edx) + : "a" (id)); + return 1; +#else int have_cpuid = 0; #ifndef _MSC_VER __asm__ __volatile__ ( @@ -620,6 +741,7 @@ cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx) return 1; } return 0; +#endif } /* @@ -649,6 +771,13 @@ void mono_arch_init (void) { InitializeCriticalSection (&mini_arch_mutex); + + ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ); + bp_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT); + mono_mprotect (bp_trigger_page, mono_pagesize (), 0); + + mono_aot_register_jit_icall ("mono_x86_throw_exception", mono_x86_throw_exception); + mono_aot_register_jit_icall ("mono_x86_throw_corlib_exception", mono_x86_throw_corlib_exception); } /* @@ -666,10 +795,16 @@ mono_arch_cleanup (void) guint32 mono_arch_cpu_optimizazions (guint32 *exclude_mask) { +#if !defined(__native_client__) int eax, ebx, ecx, edx; guint32 opts = 0; *exclude_mask = 0; + + if (mono_aot_only) + /* The cpuid function allocates from the global codeman */ + return opts; + /* Feature Flags function, flags returned in EDX. */ if (cpuid (1, &eax, &ebx, &ecx, &edx)) { if (edx & (1 << 15)) { @@ -692,6 +827,9 @@ mono_arch_cpu_optimizazions (guint32 *exclude_mask) #endif } return opts; +#else + return MONO_OPT_CMOV | MONO_OPT_FCMOV | MONO_OPT_SSE2; +#endif } /* @@ -699,7 +837,6 @@ mono_arch_cpu_optimizazions (guint32 *exclude_mask) * * Returns a bitmask corresponding to all supported versions. * - * TODO detect other versions like SSE4a. */ guint32 mono_arch_cpu_enumerate_simd_versions (void) @@ -707,20 +844,38 @@ mono_arch_cpu_enumerate_simd_versions (void) int eax, ebx, ecx, edx; guint32 sse_opts = 0; + if (mono_aot_only) + /* The cpuid function allocates from the global codeman */ + return sse_opts; + if (cpuid (1, &eax, &ebx, &ecx, &edx)) { if (edx & (1 << 25)) - sse_opts |= 1 << SIMD_VERSION_SSE1; + sse_opts |= SIMD_VERSION_SSE1; if (edx & (1 << 26)) - sse_opts |= 1 << SIMD_VERSION_SSE2; + sse_opts |= SIMD_VERSION_SSE2; if (ecx & (1 << 0)) - sse_opts |= 1 << SIMD_VERSION_SSE3; + sse_opts |= SIMD_VERSION_SSE3; if (ecx & (1 << 9)) - sse_opts |= 1 << SIMD_VERSION_SSSE3; + sse_opts |= SIMD_VERSION_SSSE3; if (ecx & (1 << 19)) - sse_opts |= 1 << SIMD_VERSION_SSE41; + sse_opts |= SIMD_VERSION_SSE41; if (ecx & (1 << 20)) - sse_opts |= 1 << SIMD_VERSION_SSE42; + sse_opts |= SIMD_VERSION_SSE42; + } + + /* Yes, all this needs to be done to check for sse4a. + See: "Amd: CPUID Specification" + */ + if (cpuid (0x80000000, &eax, &ebx, &ecx, &edx)) { + /* eax greater or equal than 0x80000001, ebx = 'htuA', ecx = DMAc', edx = 'itne'*/ + if ((((unsigned int) eax) >= 0x80000001) && (ebx == 0x68747541) && (ecx == 0x444D4163) && (edx == 0x69746E65)) { + cpuid (0x80000001, &eax, &ebx, &ecx, &edx); + if (ecx & (1 << 6)) + sse_opts |= SIMD_VERSION_SSE4a; + } } + + return sse_opts; } @@ -871,10 +1026,15 @@ needs_stack_frame (MonoCompile *cfg) MonoMethodHeader *header; gboolean result = FALSE; +#if defined(__APPLE__) + /*OSX requires stack frame code to have the correct alignment. */ + return TRUE; +#endif + if (cfg->arch.need_stack_frame_inited) return cfg->arch.need_stack_frame; - header = mono_method_get_header (cfg->method); + header = cfg->header; sig = mono_method_signature (cfg->method); if (cfg->disable_omit_fp) @@ -919,7 +1079,7 @@ mono_arch_allocate_vars (MonoCompile *cfg) gint32 *offsets; CallInfo *cinfo; - header = mono_method_get_header (cfg->method); + header = cfg->header; sig = mono_method_signature (cfg->method); cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig, FALSE); @@ -959,6 +1119,13 @@ mono_arch_allocate_vars (MonoCompile *cfg) /* Allocate locals */ offsets = mono_allocate_stack_slots (cfg, &locals_stack_size, &locals_stack_align); + if (locals_stack_size > MONO_ARCH_MAX_FRAME_SIZE) { + char *mname = mono_method_full_name (cfg->method, TRUE); + cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM; + cfg->exception_message = g_strdup_printf ("Method %s stack is too big.", mname); + g_free (mname); + return; + } if (locals_stack_align) { offset += (locals_stack_align - 1); offset &= ~(locals_stack_align - 1); @@ -1112,8 +1279,8 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) int i, n; CallInfo *cinfo; ArgInfo *ainfo; - int j; LLVMCallInfo *linfo; + MonoType *t; n = sig->param_count + sig->hasthis; @@ -1146,6 +1313,7 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage == ArgInIReg) { /* Vtype returned using a hidden argument */ linfo->ret.storage = LLVMArgVtypeRetAddr; + linfo->vret_arg_index = cinfo->vret_arg_index; } if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage != ArgInIReg) { @@ -1157,6 +1325,11 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) for (i = 0; i < n; ++i) { ainfo = cinfo->args + i; + if (i >= sig->hasthis) + t = sig->params [i - sig->hasthis]; + else + t = &mono_defaults.int_class->byval_arg; + linfo->args [i].storage = LLVMArgNone; switch (ainfo->storage) { @@ -1168,16 +1341,19 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) linfo->args [i].storage = LLVMArgInFPReg; break; case ArgOnStack: - if ((i >= sig->hasthis) && (MONO_TYPE_ISSTRUCT(sig->params [i - sig->hasthis]))) { - linfo->args [i].storage = LLVMArgVtypeByVal; + if (MONO_TYPE_ISSTRUCT (t)) { + if (mono_class_value_size (mono_class_from_mono_type (t), NULL) == 0) + /* LLVM seems to allocate argument space for empty structures too */ + linfo->args [i].storage = LLVMArgNone; + else + linfo->args [i].storage = LLVMArgVtypeByVal; } else { linfo->args [i].storage = LLVMArgInIReg; - if (!sig->params [i - sig->hasthis]->byref) { - if (sig->params [i - sig->hasthis]->type == MONO_TYPE_R4) { + if (t->byref) { + if (t->type == MONO_TYPE_R4) linfo->args [i].storage = LLVMArgInFPReg; - } else if (sig->params [i - sig->hasthis]->type == MONO_TYPE_R8) { + else if (t->type == MONO_TYPE_R8) linfo->args [i].storage = LLVMArgInFPReg; - } } } break; @@ -1255,6 +1431,15 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) ArgInfo *ainfo = cinfo->args + i; MonoType *t; + if (cinfo->vtype_retaddr && cinfo->vret_arg_index == 1 && i == 0) { + /* Push the vret arg before the first argument */ + MonoInst *vtarg; + MONO_INST_NEW (cfg, vtarg, OP_X86_PUSH); + vtarg->type = STACK_MP; + vtarg->sreg1 = call->vret_var->dreg; + MONO_ADD_INS (cfg->cbb, vtarg); + } + if (i >= sig->hasthis) t = sig->params [i - sig->hasthis]; else @@ -1342,7 +1527,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) MONO_ADD_INS (cfg->cbb, vtarg); mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE); - } else { + } else if (cinfo->vtype_retaddr && cinfo->vret_arg_index == 0) { MonoInst *vtarg; MONO_INST_NEW (cfg, vtarg, OP_X86_PUSH); vtarg->type = STACK_MP; @@ -1350,8 +1535,9 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) MONO_ADD_INS (cfg->cbb, vtarg); } - /* if the function returns a struct, the called method already does a ret $0x4 */ - cinfo->stack_usage -= 4; + /* if the function returns a struct on stack, the called method already does a ret $0x4 */ + if (cinfo->ret.storage != ArgValuetypeInReg) + cinfo->stack_usage -= 4; } call->stack_usage = cinfo->stack_usage; @@ -1452,39 +1638,49 @@ void* mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments, gboolean preserve_argument_registers) { guchar *code = p; - int arg_size = 0, save_mode = SAVE_NONE; + int arg_size = 0, stack_usage = 0, save_mode = SAVE_NONE; MonoMethod *method = cfg->method; - - switch (mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret)->type) { + MonoType *ret_type = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret); + + switch (ret_type->type) { case MONO_TYPE_VOID: /* special case string .ctor icall */ - if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class) + if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class) { save_mode = SAVE_EAX; - else + stack_usage = enable_arguments ? 8 : 4; + } else save_mode = SAVE_NONE; break; case MONO_TYPE_I8: case MONO_TYPE_U8: save_mode = SAVE_EAX_EDX; + stack_usage = enable_arguments ? 16 : 8; break; case MONO_TYPE_R4: case MONO_TYPE_R8: save_mode = SAVE_FP; + stack_usage = enable_arguments ? 16 : 8; break; case MONO_TYPE_GENERICINST: - if (!mono_type_generic_inst_is_valuetype (mono_method_signature (method)->ret)) { + if (!mono_type_generic_inst_is_valuetype (ret_type)) { save_mode = SAVE_EAX; + stack_usage = enable_arguments ? 8 : 4; break; } /* Fall through */ case MONO_TYPE_VALUETYPE: + // FIXME: Handle SMALL_STRUCT_IN_REG here for proper alignment on darwin-x86 save_mode = SAVE_STRUCT; + stack_usage = enable_arguments ? 4 : 0; break; default: save_mode = SAVE_EAX; + stack_usage = enable_arguments ? 8 : 4; break; } + x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - stack_usage - 4); + switch (save_mode) { case SAVE_EAX_EDX: x86_push_reg (code, X86_EDX); @@ -1532,6 +1728,7 @@ mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolea mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_ABS, func); x86_call_code (code, 0); } + x86_alu_reg_imm (code, X86_ADD, X86_ESP, arg_size + 4); switch (save_mode) { @@ -1550,6 +1747,8 @@ mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolea default: break; } + + x86_alu_reg_imm (code, X86_ADD, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - stack_usage); return code; } @@ -1846,7 +2045,7 @@ mono_emit_stack_alloc (guchar *code, MonoInst* tree) int sreg = tree->sreg1; int need_touch = FALSE; -#if defined(PLATFORM_WIN32) || defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK) +#if defined(TARGET_WIN32) || defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK) need_touch = TRUE; #endif @@ -1966,6 +2165,23 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code) return code; } +gboolean +mono_x86_have_tls_get (void) +{ +#ifdef __APPLE__ + guint32 *ins = (guint32*)pthread_getspecific; + /* + * We're looking for these two instructions: + * + * mov 0x4(%esp),%eax + * mov %gs:0x48(,%eax,4),%eax + */ + return ins [0] == 0x0424448b && ins [1] == 0x85048b65 && ins [2] == 0x00000048; +#else + return TRUE; +#endif +} + /* * mono_x86_emit_tls_get: * @code: buffer to store code to @@ -1981,7 +2197,10 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code) guint8* mono_x86_emit_tls_get (guint8* code, int dreg, int tls_offset) { -#ifdef PLATFORM_WIN32 +#if defined(__APPLE__) + x86_prefix (code, X86_GS_PREFIX); + x86_mov_reg_mem (code, dreg, 0x48 + tls_offset * 4, 4); +#elif defined(TARGET_WIN32) /* * See the Under the Hood article in the May 1996 issue of Microsoft Systems * Journal and/or a disassembly of the TlsGet () function. @@ -2067,6 +2286,11 @@ x86_pop_reg (code, X86_ECX); \ x86_pop_reg (code, X86_EDX); \ x86_pop_reg (code, X86_EAX); +/* REAL_PRINT_REG does not appear to be used, and was not adapted to work with Native Client. */ +#ifdef __native__client_codegen__ +#define REAL_PRINT_REG(text, reg) g_assert_not_reached() +#endif + /* benchmark and set based on cpu */ #define LOOP_ALIGNMENT 8 #define bb_is_loop_start(bb) ((bb)->loop_body_start && (bb)->nesting) @@ -2093,7 +2317,23 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) bb->native_offset = cfg->code_len; } } - +#ifdef __native_client_codegen__ + { + /* For Native Client, all indirect call/jump targets must be */ + /* 32-byte aligned. Exception handler blocks are jumped to */ + /* indirectly as well. */ + gboolean bb_needs_alignment = (bb->flags & BB_INDIRECT_JUMP_TARGET) || + (bb->flags & BB_EXCEPTION_HANDLER); + + /* if ((cfg->code_len & kNaClAlignmentMask) != 0) { */ + if ( bb_needs_alignment && ((cfg->code_len & kNaClAlignmentMask) != 0)) { + int pad = kNaClAlignment - (cfg->code_len & kNaClAlignmentMask); + if (pad != kNaClAlignment) code = mono_arch_nacl_pad(code, pad); + cfg->code_len += pad; + bb->native_offset = cfg->code_len; + } + } +#endif /* __native_client_codegen__ */ if (cfg->verbose_level > 2) g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset); @@ -2113,14 +2353,19 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) mono_debug_open_block (cfg, bb, offset); + if (mono_break_at_bb_method && mono_method_desc_full_match (mono_break_at_bb_method, cfg->method) && bb->block_num == mono_break_at_bb_bb_num) + x86_breakpoint (code); + MONO_BB_FOR_EACH_INS (bb, ins) { offset = code - cfg->native_code; max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN]; - if (G_UNLIKELY (offset > (cfg->code_size - max_len - 16))) { +#define EXTRA_CODE_SPACE (NACL_SIZE (16, 16 + kNaClAlignment)) + + if (G_UNLIKELY (offset > (cfg->code_size - max_len - EXTRA_CODE_SPACE))) { cfg->code_size *= 2; - cfg->native_code = g_realloc (cfg->native_code, cfg->code_size); + cfg->native_code = mono_realloc_native_code(cfg); code = cfg->native_code + offset; mono_jit_stats.code_reallocs++; } @@ -2310,6 +2555,31 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_NOT_REACHED: case OP_NOT_NULL: break; + case OP_SEQ_POINT: { + int i; + + if (cfg->compile_aot) + NOT_IMPLEMENTED; + + /* + * Read from the single stepping trigger page. This will cause a + * SIGSEGV when single stepping is enabled. + * We do this _before_ the breakpoint, so single stepping after + * a breakpoint is hit will step to the next IL offset. + */ + if (ins->flags & MONO_INST_SINGLE_STEP_LOC) + x86_alu_reg_mem (code, X86_CMP, X86_EAX, (guint32)ss_trigger_page); + + mono_add_seq_point (cfg, bb, ins, code - cfg->native_code); + + /* + * A placeholder for a possible breakpoint inserted by + * mono_arch_set_breakpoint (). + */ + for (i = 0; i < 6; ++i) + x86_nop (code); + break; + } case OP_ADDCC: case OP_IADDCC: case OP_IADD: @@ -2404,6 +2674,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) */ x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EDX); x86_alu_reg_reg (code, X86_SUB, X86_EAX, X86_EDX); + } else if (power == 0) { + x86_alu_reg_reg (code, X86_XOR, ins->dreg, ins->dreg); } else { /* Based on gcc code */ @@ -2661,14 +2933,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) x86_mov_reg_imm (code, ins->dreg, 0); break; case OP_LOAD_GOTADDR: - x86_call_imm (code, 0); - /* - * The patch needs to point to the pop, since the GOT offset needs - * to be added to that address. - */ - mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_GOT_OFFSET, NULL); - x86_pop_reg (code, ins->dreg); - x86_alu_reg_imm (code, X86_ADD, ins->dreg, 0xf0f0f0f0); + g_assert (ins->dreg == MONO_ARCH_GOT_REG); + code = mono_arch_emit_load_got_addr (cfg->native_code, code, cfg, NULL); break; case OP_GOT_ENTRY: mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_right->inst_i1, ins->inst_right->inst_p0); @@ -2797,13 +3063,6 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_CALL_MEMBASE: call = (MonoCallInst*)ins; - /* - * Emit a few nops to simplify get_vcall_slot (). - */ - x86_nop (code); - x86_nop (code); - x86_nop (code); - x86_call_membase (code, ins->sreg1, ins->inst_offset); if (call->stack_usage && !CALLCONV_IS_STDCALL (call->signature)) { if (call->stack_usage == 4) @@ -2874,12 +3133,14 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) break; } case OP_THROW: { + x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 4); x86_push_reg (code, ins->sreg1); code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_arch_throw_exception"); break; } case OP_RETHROW: { + x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 4); x86_push_reg (code, ins->sreg1); code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_arch_rethrow_exception"); @@ -2889,6 +3150,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 4); mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb); x86_call_imm (code, 0); + mono_cfg_add_try_hole (cfg, ins->inst_eh_block, code, bb); x86_alu_reg_imm (code, X86_ADD, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 4); break; case OP_START_HANDLER: { @@ -3050,10 +3312,6 @@ 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; @@ -3143,7 +3401,6 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) x86_fld_membase (code, X86_ESP, 0, FALSE); x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8); break; - case OP_LCONV_TO_R_UN: case OP_LCONV_TO_R_UN_2: { static guint8 mn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x40 }; guint8 *br; @@ -3737,17 +3994,59 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) break; } case OP_ATOMIC_CAS_I4: { + g_assert (ins->dreg == X86_EAX); g_assert (ins->sreg3 == X86_EAX); g_assert (ins->sreg1 != X86_EAX); g_assert (ins->sreg1 != ins->sreg2); x86_prefix (code, X86_LOCK_PREFIX); x86_cmpxchg_membase_reg (code, ins->sreg1, ins->inst_offset, ins->sreg2); + break; + } +#ifdef HAVE_SGEN_GC + case OP_CARD_TABLE_WBARRIER: { + int ptr = ins->sreg1; + int value = ins->sreg2; + guchar *br; + int nursery_shift, card_table_shift; + gpointer card_table_mask; + size_t nursery_size; + gulong card_table = (gulong)mono_gc_get_card_table (&card_table_shift, &card_table_mask); + gulong nursery_start = (gulong)mono_gc_get_nursery (&nursery_shift, &nursery_size); - if (ins->dreg != X86_EAX) - x86_mov_reg_reg (code, ins->dreg, X86_EAX, 4); + /* + * We need one register we can clobber, we choose EDX and make sreg1 + * fixed EAX to work around limitations in the local register allocator. + * sreg2 might get allocated to EDX, but that is not a problem since + * we use it before clobbering EDX. + */ + g_assert (ins->sreg1 == X86_EAX); + + /* + * This is the code we produce: + * + * edx = value + * edx >>= nursery_shift + * cmp edx, (nursery_start >> nursery_shift) + * jne done + * edx = ptr + * edx >>= card_table_shift + * card_table[edx] = 1 + * done: + */ + + if (value != X86_EDX) + x86_mov_reg_reg (code, X86_EDX, value, 4); + x86_shift_reg_imm (code, X86_SHR, X86_EDX, nursery_shift); + x86_alu_reg_imm (code, X86_CMP, X86_EDX, nursery_start >> nursery_shift); + br = code; x86_branch8 (code, X86_CC_NE, -1, FALSE); + x86_mov_reg_reg (code, X86_EDX, ptr, 4); + x86_shift_reg_imm (code, X86_SHR, X86_EDX, card_table_shift); + x86_mov_membase_imm (code, X86_EDX, card_table, 1, 1); + x86_patch (br, code); break; } +#endif #ifdef MONO_ARCH_SIMD_INTRINSICS case OP_ADDPS: x86_sse_alu_ps_reg_reg (code, X86_SSE_ADD, ins->sreg1, ins->sreg2); @@ -3855,6 +4154,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_XORPD: x86_sse_alu_pd_reg_reg (code, X86_SSE_XOR, ins->sreg1, ins->sreg2); break; + case OP_SQRTPD: + x86_sse_alu_pd_reg_reg (code, X86_SSE_SQRT, ins->dreg, ins->sreg1); + break; case OP_ADDSUBPD: x86_sse_alu_pd_reg_reg (code, X86_SSE_ADDSUB, ins->sreg1, ins->sreg2); break; @@ -4304,9 +4606,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) } if (G_UNLIKELY ((code - cfg->native_code - offset) > max_len)) { +#ifndef __native_client_codegen__ g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)", - mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset); + mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset); g_assert_not_reached (); +#endif /* __native_client_codegen__ */ } cpos += max_len; @@ -4389,13 +4693,30 @@ mono_arch_emit_prolog (MonoCompile *cfg) int alloc_size, pos, max_offset, i, cfa_offset; guint8 *code; gboolean need_stack_frame; +#ifdef __native_client_codegen__ + guint alignment_check; +#endif - cfg->code_size = MAX (mono_method_get_header (method)->code_size * 4, 10240); + cfg->code_size = MAX (cfg->header->code_size * 4, 10240); if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) cfg->code_size += 512; +#ifdef __native_client_codegen__ + /* native_code_alloc is not 32-byte aligned, native_code is. */ + cfg->native_code_alloc = g_malloc (cfg->code_size + kNaClAlignment); + + /* Align native_code to next nearest kNaclAlignment byte. */ + cfg->native_code = (guint)cfg->native_code_alloc + kNaClAlignment; + cfg->native_code = (guint)cfg->native_code & ~kNaClAlignmentMask; + + code = cfg->native_code; + + alignment_check = (guint)cfg->native_code & kNaClAlignmentMask; + g_assert(alignment_check == 0); +#else code = cfg->native_code = g_malloc (cfg->code_size); +#endif /* Offset between RSP and the CFA */ cfa_offset = 0; @@ -4439,17 +4760,28 @@ mono_arch_emit_prolog (MonoCompile *cfg) 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); x86_patch (buf, code); -#ifdef PLATFORM_WIN32 +#ifdef TARGET_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)); #endif } else { - g_assert (!cfg->compile_aot); - x86_push_imm (code, cfg->domain); + if (cfg->compile_aot) { + /* + * This goes before the saving of callee saved regs, so save the got reg + * ourselves. + */ + x86_push_reg (code, MONO_ARCH_GOT_REG); + code = mono_arch_emit_load_got_addr (cfg->native_code, code, cfg, NULL); + x86_push_imm (code, 0); + } else { + 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); + if (cfg->compile_aot) + x86_pop_reg (code, MONO_ARCH_GOT_REG); } } @@ -4457,8 +4789,13 @@ mono_arch_emit_prolog (MonoCompile *cfg) pos += sizeof (MonoLMF); /* save the current IP */ - mono_add_patch_info (cfg, code + 1 - cfg->native_code, MONO_PATCH_INFO_IP, NULL); - x86_push_imm_template (code); + if (cfg->compile_aot) { + /* This pushes the current ip */ + x86_call_imm (code, 0); + } else { + mono_add_patch_info (cfg, code + 1 - cfg->native_code, MONO_PATCH_INFO_IP, NULL); + x86_push_imm_template (code); + } cfa_offset += sizeof (gpointer); /* save all caller saved regs */ @@ -4499,12 +4836,14 @@ mono_arch_emit_prolog (MonoCompile *cfg) if (lmf_addr_tls_offset != -1) { /* Load lmf quicky using the GS register */ code = mono_x86_emit_tls_get (code, X86_EAX, lmf_addr_tls_offset); -#ifdef PLATFORM_WIN32 +#ifdef TARGET_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)); #endif } else { + if (cfg->compile_aot) + code = mono_arch_emit_load_got_addr (cfg->native_code, code, cfg, NULL); code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_get_lmf_addr"); } @@ -4556,8 +4895,18 @@ mono_arch_emit_prolog (MonoCompile *cfg) if (alloc_size) { /* See mono_emit_stack_alloc */ -#if defined(PLATFORM_WIN32) || defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK) +#if defined(TARGET_WIN32) || defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK) guint32 remaining_size = alloc_size; + /*FIXME handle unbounded code expansion, we should use a loop in case of more than X interactions*/ + guint32 required_code_size = ((remaining_size / 0x1000) + 1) * 8; /*8 is the max size of x86_alu_reg_imm + x86_test_membase_reg*/ + guint32 offset = code - cfg->native_code; + if (G_UNLIKELY (required_code_size >= (cfg->code_size - offset))) { + while (required_code_size >= (cfg->code_size - offset)) + cfg->code_size *= 2; + cfg->native_code = mono_realloc_native_code(cfg); + code = cfg->native_code + offset; + mono_jit_stats.code_reallocs++; + } while (remaining_size >= 0x1000) { x86_alu_reg_imm (code, X86_SUB, X86_ESP, 0x1000); x86_test_membase_reg (code, X86_ESP, 0, X86_ESP); @@ -4600,11 +4949,23 @@ mono_arch_emit_prolog (MonoCompile *cfg) /* max alignment for loops */ if ((cfg->opt & MONO_OPT_LOOP) && bb_is_loop_start (bb)) max_offset += LOOP_ALIGNMENT; - +#ifdef __native_client_codegen__ + /* max alignment for native client */ + max_offset += kNaClAlignment; +#endif MONO_BB_FOR_EACH_INS (bb, ins) { if (ins->opcode == OP_LABEL) ins->inst_c1 = max_offset; - +#ifdef __native_client_codegen__ + { + int space_in_block = kNaClAlignment - + ((max_offset + cfg->code_len) & kNaClAlignmentMask); + int max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN]; + if (space_in_block < max_len && max_len < kNaClAlignment) { + max_offset += space_in_block; + } + } +#endif /* __native_client_codegen__ */ max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN]; } } @@ -4659,7 +5020,7 @@ mono_arch_emit_epilog (MonoCompile *cfg) while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) { cfg->code_size *= 2; - cfg->native_code = g_realloc (cfg->native_code, cfg->code_size); + cfg->native_code = mono_realloc_native_code(cfg); mono_jit_stats.code_reallocs++; } @@ -4840,7 +5201,7 @@ mono_arch_emit_exceptions (MonoCompile *cfg) while (cfg->code_len + code_size > (cfg->code_size - 16)) { cfg->code_size *= 2; - cfg->native_code = g_realloc (cfg->native_code, cfg->code_size); + cfg->native_code = mono_realloc_native_code(cfg); mono_jit_stats.code_reallocs++; } @@ -4873,7 +5234,13 @@ mono_arch_emit_exceptions (MonoCompile *cfg) guint32 size; /* Compute size of code following the push */ +#ifdef __native_client_codegen__ + code = mono_nacl_align (code); + size = kNaClAlignment; +#else size = 5 + 5; +#endif + /*This is aligned to 16 bytes by the callee. This way we save a few bytes here.*/ if ((code - cfg->native_code) - throw_ip < 126 - size) { /* Use the shorter form */ @@ -4947,22 +5314,19 @@ mono_arch_setup_jit_tls_data (MonoJitTlsData *tls) { if (!tls_offset_inited) { if (!getenv ("MONO_NO_TLS")) { -#ifdef PLATFORM_WIN32 +#ifdef TARGET_WIN32 /* * We need to init this multiple times, since when we are first called, the key might not * be initialized yet. */ appdomain_tls_offset = mono_domain_get_tls_key (); lmf_tls_offset = mono_get_jit_tls_key (); - thread_tls_offset = mono_thread_get_tls_key (); /* Only 64 tls entries can be accessed using inline code */ if (appdomain_tls_offset >= 64) appdomain_tls_offset = -1; if (lmf_tls_offset >= 64) lmf_tls_offset = -1; - if (thread_tls_offset >= 64) - thread_tls_offset = -1; #else #if MONO_XEN_OPT optimize_for_xen = access ("/proc/xen", F_OK) == 0; @@ -4971,7 +5335,6 @@ mono_arch_setup_jit_tls_data (MonoJitTlsData *tls) 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 } } @@ -4991,10 +5354,19 @@ mono_arch_free_jit_tls_data (MonoJitTlsData *tls) //[1 + 5] x86_jump_mem(inst,mem) #define CMP_SIZE 6 +#ifdef __native_client_codegen__ +/* These constants should be coming from cpu-x86.md */ +/* I suspect the size calculation below is actually incorrect. */ +/* TODO: fix the calculation that uses these sizes. */ +#define BR_SMALL_SIZE 16 +#define BR_LARGE_SIZE 12 +#else #define BR_SMALL_SIZE 2 #define BR_LARGE_SIZE 5 +#endif /* __native_client_codegen__ */ #define JUMP_IMM_SIZE 6 #define ENABLE_WRONG_METHOD_CHECK 0 +#define DEBUG_IMT 0 static int imt_branch_distance (MonoIMTCheckItem **imt_entries, int start, int target) @@ -5016,6 +5388,9 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI int size = 0; guint8 *code, *start; +#ifdef __native_client_codegen__ + /* g_print("mono_arch_build_imt_thunk needs to be aligned.\n"); */ +#endif for (i = 0; i < count; ++i) { MonoIMTCheckItem *item = imt_entries [i]; if (item->is_equals) { @@ -5109,6 +5484,15 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI if (!fail_tramp) mono_stats.imt_thunks_size += code - start; g_assert (code - start <= size); + +#if DEBUG_IMT + { + char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count); + mono_disassemble_code (NULL, (guint8*)start, code - start, buff); + g_free (buff); + } +#endif + return start; } @@ -5117,28 +5501,6 @@ mono_arch_find_imt_method (mgreg_t *regs, guint8 *code) { return (MonoMethod*) regs [MONO_ARCH_IMT_REG]; } - -MonoObject* -mono_arch_find_this_argument (mgreg_t *regs, MonoMethod *method, MonoGenericSharingContext *gsctx) -{ - MonoMethodSignature *sig = mono_method_signature (method); - CallInfo *cinfo = get_call_info (gsctx, 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 MonoVTable* @@ -5147,6 +5509,17 @@ mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code) return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG]; } +GSList* +mono_arch_get_cie_program (void) +{ + GSList *l = NULL; + + mono_add_unwind_op_def_cfa (l, (guint8*)NULL, (guint8*)NULL, X86_ESP, 4); + mono_add_unwind_op_offset (l, (guint8*)NULL, (guint8*)NULL, X86_NREG, -4); + + return l; +} + MonoInst* mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) { @@ -5232,18 +5605,6 @@ MonoInst* mono_arch_get_domain_intrinsic (MonoCompile* cfg) return ins; } -MonoInst* mono_arch_get_thread_intrinsic (MonoCompile* cfg) -{ - MonoInst* ins; - - if (thread_tls_offset == -1) - return NULL; - - MONO_INST_NEW (cfg, ins, OP_TLS_GET); - ins->inst_offset = thread_tls_offset; - return ins; -} - guint32 mono_arch_get_patch_offset (guint8 *code) { @@ -5321,52 +5682,16 @@ mono_breakpoint_clean_code (guint8 *method_start, guint8 *code, int offset, guin return can_write; } -gpointer -mono_arch_get_vcall_slot (guint8 *code, mgreg_t *regs, int *displacement) +/* + * mono_x86_get_this_arg_offset: + * + * Return the offset of the stack location where this is passed during a virtual + * call. + */ +guint32 +mono_x86_get_this_arg_offset (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig) { - guint8 buf [8]; - guint8 reg = 0; - gint32 disp = 0; - - mono_breakpoint_clean_code (NULL, code, 8, buf, sizeof (buf)); - code = buf + 8; - - *displacement = 0; - - code -= 6; - - /* - * A given byte sequence can match more than case here, so we have to be - * really careful about the ordering of the cases. Longer sequences - * come first. - * There are two types of calls: - * - direct calls: 0xff address_byte 8/32 bits displacement - * - indirect calls: nop nop nop - * The nops make sure we don't confuse the instruction preceeding an indirect - * call with a direct call. - */ - if ((code [1] != 0xe8) && (code [3] == 0xff) && ((code [4] & 0x18) == 0x10) && ((code [4] >> 6) == 1)) { - reg = code [4] & 0x07; - disp = (signed char)code [5]; - } else if ((code [0] == 0xff) && ((code [1] & 0x18) == 0x10) && ((code [1] >> 6) == 2)) { - reg = code [1] & 0x07; - disp = *((gint32*)(code + 2)); - } else if ((code [1] == 0xe8)) { - return NULL; - } else if ((code [4] == 0xff) && (((code [5] >> 6) & 0x3) == 0) && (((code [5] >> 3) & 0x7) == 2)) { - /* - * This is a interface call - * 8b 40 30 mov 0x30(%eax),%eax - * ff 10 call *(%eax) - */ - disp = 0; - reg = code [5] & 0x07; - } - else - return NULL; - - *displacement = disp; - return (gpointer)regs [reg]; + return 0; } gpointer @@ -5378,25 +5703,12 @@ mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSi gpointer res; int offset; - /* - * Avoid expensive calls to get_generic_context_from_code () + get_call_info - * if possible. - */ - if (MONO_TYPE_ISSTRUCT (sig->ret)) { - if (!gsctx && code) - gsctx = mono_get_generic_context_from_code (code); - cinfo = get_call_info (gsctx, NULL, sig, FALSE); - - offset = cinfo->args [0].offset; - } else { - offset = 0; - } + offset = 0; /* * The stack looks like: * * - * * * <4 pointers pushed by mono_arch_create_trampoline_code ()> */ @@ -5408,18 +5720,11 @@ mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSi #define MAX_ARCH_DELEGATE_PARAMS 10 -gpointer -mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target) +static gpointer +get_delegate_invoke_impl (gboolean has_target, guint32 param_count, guint32 *code_len) { guint8 *code, *start; - if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS) - return NULL; - - /* FIXME: Support more cases */ - if (MONO_TYPE_ISSTRUCT (sig->ret)) - return NULL; - /* * The stack contains: * @@ -5427,10 +5732,6 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe */ if (has_target) { - static guint8* cached = NULL; - if (cached) - return cached; - start = code = mono_global_codeman_reserve (64); /* Replace the this argument with the target */ @@ -5440,26 +5741,15 @@ 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); - - mono_debug_add_delegate_trampoline (start, code - start); - - mono_memory_barrier (); - - cached = start; } else { - 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; - - code = cache [sig->param_count]; - if (code) - return code; - +#ifdef __native_client_codegen__ + /* TODO: calculate this size correctly */ + int code_reserve = 13 + (param_count * 8) + 2 * kNaClAlignment; +#else + int code_reserve = 8 + (param_count * 8); +#endif /* __native_client_codegen__ */ /* * The stack contains: * @@ -5482,7 +5772,7 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe x86_mov_reg_membase (code, X86_ECX, X86_ESP, 4, 4); /* move args up */ - for (i = 0; i < sig->param_count; ++i) { + for (i = 0; i < 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); } @@ -5490,8 +5780,85 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe x86_jump_membase (code, X86_ECX, G_STRUCT_OFFSET (MonoDelegate, method_ptr)); g_assert ((code - start) < code_reserve); + } + + mono_debug_add_delegate_trampoline (start, code - start); + + if (code_len) + *code_len = code - start; + + return start; +} + +GSList* +mono_arch_get_delegate_invoke_impls (void) +{ + GSList *res = NULL; + guint8 *code; + guint32 code_len; + int i; + + code = get_delegate_invoke_impl (TRUE, 0, &code_len); + res = g_slist_prepend (res, mono_tramp_info_create (g_strdup ("delegate_invoke_impl_has_target"), code, code_len, NULL, NULL)); + + for (i = 0; i < MAX_ARCH_DELEGATE_PARAMS; ++i) { + code = get_delegate_invoke_impl (FALSE, i, &code_len); + res = g_slist_prepend (res, mono_tramp_info_create (g_strdup_printf ("delegate_invoke_impl_target_%d", i), code, code_len, NULL, NULL)); + } + + return res; +} + +gpointer +mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target) +{ + guint8 *code, *start; - mono_debug_add_delegate_trampoline (start, code - start); + if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS) + return NULL; + + /* FIXME: Support more cases */ + if (MONO_TYPE_ISSTRUCT (sig->ret)) + return NULL; + + /* + * The stack contains: + * + * + */ + + if (has_target) { + static guint8* cached = NULL; + if (cached) + return cached; + + if (mono_aot_only) + start = mono_aot_get_trampoline ("delegate_invoke_impl_has_target"); + else + start = get_delegate_invoke_impl (TRUE, 0, NULL); + + mono_memory_barrier (); + + cached = start; + } else { + static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL}; + int i = 0; + + for (i = 0; i < sig->param_count; ++i) + if (!mono_is_regsize_var (sig->params [i])) + return NULL; + + code = cache [sig->param_count]; + if (code) + return code; + + if (mono_aot_only) { + char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count); + start = mono_aot_get_trampoline (name); + g_free (name); + } else { + start = get_delegate_invoke_impl (FALSE, sig->param_count, NULL); + } mono_memory_barrier (); @@ -5683,3 +6050,286 @@ mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins) #endif /* MONO_ARCH_SIMD_INTRINSICS */ } +/*MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD*/ +gpointer +mono_arch_install_handler_block_guard (MonoJitInfo *ji, MonoJitExceptionInfo *clause, MonoContext *ctx, gpointer new_value) +{ + int offset; + gpointer *sp, old_value; + char *bp; + const unsigned char *handler; + + /*Decode the first instruction to figure out where did we store the spvar*/ + /*Our jit MUST generate the following: + mov %esp, -?(%ebp) + Which is encoded as: 0x89 mod_rm. + mod_rm (esp, ebp, imm) which can be: (imm will never be zero) + mod (reg + imm8): 01 reg(esp): 100 rm(ebp): 101 -> 01100101 (0x65) + mod (reg + imm32): 10 reg(esp): 100 rm(ebp): 101 -> 10100101 (0xA5) + */ + handler = clause->handler_start; + + if (*handler != 0x89) + return NULL; + + ++handler; + + if (*handler == 0x65) + offset = *(signed char*)(handler + 1); + else if (*handler == 0xA5) + offset = *(int*)(handler + 1); + else + return NULL; + + /*Load the spvar*/ + bp = MONO_CONTEXT_GET_BP (ctx); + sp = *(gpointer*)(bp + offset); + + old_value = *sp; + if (old_value < ji->code_start || (char*)old_value > ((char*)ji->code_start + ji->code_size)) + return old_value; + + *sp = new_value; + + return old_value; +} + +/* + * mono_aot_emit_load_got_addr: + * + * Emit code to load the got address. + * On x86, the result is placed into EBX. + */ +guint8* +mono_arch_emit_load_got_addr (guint8 *start, guint8 *code, MonoCompile *cfg, MonoJumpInfo **ji) +{ + x86_call_imm (code, 0); + /* + * The patch needs to point to the pop, since the GOT offset needs + * to be added to that address. + */ + if (cfg) + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_GOT_OFFSET, NULL); + else + *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_GOT_OFFSET, NULL); + x86_pop_reg (code, MONO_ARCH_GOT_REG); + x86_alu_reg_imm (code, X86_ADD, MONO_ARCH_GOT_REG, 0xf0f0f0f0); + + return code; +} + +/* + * mono_ppc_emit_load_aotconst: + * + * Emit code to load the contents of the GOT slot identified by TRAMP_TYPE and + * TARGET from the mscorlib GOT in full-aot code. + * On x86, the GOT address is assumed to be in EBX, and the result is placed into + * EAX. + */ +guint8* +mono_arch_emit_load_aotconst (guint8 *start, guint8 *code, MonoJumpInfo **ji, int tramp_type, gconstpointer target) +{ + /* Load the mscorlib got address */ + x86_mov_reg_membase (code, X86_EAX, MONO_ARCH_GOT_REG, sizeof (gpointer), 4); + *ji = mono_patch_info_list_prepend (*ji, code - start, tramp_type, target); + /* arch_emit_got_access () patches this */ + x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0xf0f0f0f0, 4); + + return code; +} + +/* Can't put this into mini-x86.h */ +gpointer +mono_x86_get_signal_exception_trampoline (MonoTrampInfo **info, gboolean aot); + +GSList * +mono_arch_get_trampolines (gboolean aot) +{ + MonoTrampInfo *info; + GSList *tramps = NULL; + + mono_x86_get_signal_exception_trampoline (&info, aot); + + tramps = g_slist_append (tramps, info); + + return tramps; +} + + +#if __APPLE__ +#define DBG_SIGNAL SIGBUS +#else +#define DBG_SIGNAL SIGSEGV +#endif + +/* Soft Debug support */ +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED + +/* + * mono_arch_set_breakpoint: + * + * Set a breakpoint at the native code corresponding to JI at NATIVE_OFFSET. + * The location should contain code emitted by OP_SEQ_POINT. + */ +void +mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip) +{ + guint8 *code = ip; + + /* + * In production, we will use int3 (has to fix the size in the md + * file). But that could confuse gdb, so during development, we emit a SIGSEGV + * instead. + */ + g_assert (code [0] == 0x90); + x86_alu_reg_mem (code, X86_CMP, X86_EAX, (guint32)bp_trigger_page); +} + +/* + * mono_arch_clear_breakpoint: + * + * Clear the breakpoint at IP. + */ +void +mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip) +{ + guint8 *code = ip; + int i; + + for (i = 0; i < 6; ++i) + x86_nop (code); +} + +/* + * mono_arch_start_single_stepping: + * + * Start single stepping. + */ +void +mono_arch_start_single_stepping (void) +{ + mono_mprotect (ss_trigger_page, mono_pagesize (), 0); +} + +/* + * mono_arch_stop_single_stepping: + * + * Stop single stepping. + */ +void +mono_arch_stop_single_stepping (void) +{ + mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ); +} + +/* + * mono_arch_is_single_step_event: + * + * Return whenever the machine state in SIGCTX corresponds to a single + * step event. + */ +gboolean +mono_arch_is_single_step_event (void *info, void *sigctx) +{ +#ifdef TARGET_WIN32 + EXCEPTION_RECORD* einfo = (EXCEPTION_RECORD*)info; /* Sometimes the address is off by 4 */ + if ((einfo->ExceptionInformation[1] >= ss_trigger_page && (guint8*)einfo->ExceptionInformation[1] <= (guint8*)ss_trigger_page + 128)) + return TRUE; + else + return FALSE; +#else + siginfo_t* sinfo = (siginfo_t*) info; + /* Sometimes the address is off by 4 */ + if (sinfo->si_signo == DBG_SIGNAL && (sinfo->si_addr >= ss_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)ss_trigger_page + 128)) + return TRUE; + else + return FALSE; +#endif +} + +gboolean +mono_arch_is_breakpoint_event (void *info, void *sigctx) +{ +#ifdef TARGET_WIN32 + EXCEPTION_RECORD* einfo = (EXCEPTION_RECORD*)info; /* Sometimes the address is off by 4 */ + if ((einfo->ExceptionInformation[1] >= bp_trigger_page && (guint8*)einfo->ExceptionInformation[1] <= (guint8*)bp_trigger_page + 128)) + return TRUE; + else + return FALSE; +#else + siginfo_t* sinfo = (siginfo_t*)info; + /* Sometimes the address is off by 4 */ + if (sinfo->si_signo == DBG_SIGNAL && (sinfo->si_addr >= bp_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)bp_trigger_page + 128)) + return TRUE; + else + return FALSE; +#endif +} + +/* + * mono_arch_get_ip_for_breakpoint: + * + * See mini-amd64.c for docs. + */ +guint8* +mono_arch_get_ip_for_breakpoint (MonoJitInfo *ji, MonoContext *ctx) +{ + guint8 *ip = MONO_CONTEXT_GET_IP (ctx); + + return ip; +} + +#define BREAKPOINT_SIZE 6 + +/* + * mono_arch_get_ip_for_single_step: + * + * See mini-amd64.c for docs. + */ +guint8* +mono_arch_get_ip_for_single_step (MonoJitInfo *ji, MonoContext *ctx) +{ + guint8 *ip = MONO_CONTEXT_GET_IP (ctx); + + /* Size of x86_alu_reg_imm */ + ip += 6; + + return ip; +} + +/* + * mono_arch_skip_breakpoint: + * + * See mini-amd64.c for docs. + */ +void +mono_arch_skip_breakpoint (MonoContext *ctx) +{ + MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + BREAKPOINT_SIZE); +} + +/* + * mono_arch_skip_single_step: + * + * See mini-amd64.c for docs. + */ +void +mono_arch_skip_single_step (MonoContext *ctx) +{ + MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 6); +} + +/* + * mono_arch_get_seq_point_info: + * + * See mini-amd64.c for docs. + */ +gpointer +mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code) +{ + NOT_IMPLEMENTED; + return NULL; +} + +#endif +