X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini-arm.c;h=0a72637f062ad53b5efd0e90572c5925241fab7c;hb=2d23bfcbce7a3f7e54dcd5911adb88b244baca35;hp=be29c4a7379fb90833607b68e1693a0bb1e36a93;hpb=87d0c0348a3cdd17ea1ca4d6f6563c9f94aa7985;p=mono.git diff --git a/mono/mini/mini-arm.c b/mono/mini/mini-arm.c index be29c4a7379..0a72637f062 100644 --- a/mono/mini/mini-arm.c +++ b/mono/mini/mini-arm.c @@ -12,6 +12,7 @@ #include #include +#include #include "mini-arm.h" #include "cpu-arm.h" @@ -36,8 +37,25 @@ static gint lmf_addr_tls_offset = -1; static CRITICAL_SECTION mini_arch_mutex; static int v5_supported = 0; +static int v7_supported = 0; static int thumb_supported = 0; +/* + * 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; + +/* Structure used by the sequence points in AOTed code */ +typedef struct { + gpointer ss_trigger_page; + gpointer bp_trigger_page; + guint8* bp_addrs [MONO_ZERO_LEN_ARRAY]; +} SeqPointInfo; + /* * TODO: * floating point support: on ARM it is a mess, there are at least 3 @@ -65,6 +83,27 @@ int mono_exc_esp_offset = 0; #define ADD_LR_PC_4 ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 25) | (1 << 23) | (ARMREG_PC << 16) | (ARMREG_LR << 12) | 4) #define MOV_LR_PC ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 24) | (0xa << 20) | (ARMREG_LR << 12) | ARMREG_PC) #define DEBUG_IMT 0 + +/* A variant of ARM_LDR_IMM which can handle large offsets */ +#define ARM_LDR_IMM_GENERAL(code, dreg, basereg, offset, scratch_reg) do { \ + if (arm_is_imm12 ((offset))) { \ + ARM_LDR_IMM (code, (dreg), (basereg), (offset)); \ + } else { \ + g_assert ((scratch_reg) != (basereg)); \ + code = mono_arm_emit_load_imm (code, (scratch_reg), (offset)); \ + ARM_LDR_REG_REG (code, (dreg), (basereg), (scratch_reg)); \ + } \ + } while (0) + +#define ARM_STR_IMM_GENERAL(code, dreg, basereg, offset, scratch_reg) do { \ + if (arm_is_imm12 ((offset))) { \ + ARM_STR_IMM (code, (dreg), (basereg), (offset)); \ + } else { \ + g_assert ((scratch_reg) != (basereg)); \ + code = mono_arm_emit_load_imm (code, (scratch_reg), (offset)); \ + ARM_STR_REG_REG (code, (dreg), (basereg), (scratch_reg)); \ + } \ + } while (0) const char* mono_arch_regname (int reg) @@ -97,6 +136,8 @@ mono_arch_fregname (int reg) return "unknown"; } +#ifndef DISABLE_JIT + static guint8* emit_big_add (guint8 *code, int dreg, int sreg, int imm) { @@ -208,6 +249,8 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code) return code; } +#endif /* #ifndef DISABLE_JIT */ + /* * mono_arch_get_argument_info: * @csig: a method signature @@ -273,7 +316,7 @@ decode_vcall_slot_from_ldr (guint32 ldr, mgreg_t *regs, int *displacement) if (((ldr >> 23) & 1) == 0) /*U bit, 0 means negative and 1 positive*/ offset = -offset; /*g_print ("found vcall at r%d + %d for code at %p 0x%x\n", reg, offset, code, *code);*/ - o = regs [reg]; + o = (gpointer)regs [reg]; *displacement = offset; return o; @@ -488,7 +531,11 @@ mono_arch_cpu_init (void) void mono_arch_init (void) { - InitializeCriticalSection (&mini_arch_mutex); + InitializeCriticalSection (&mini_arch_mutex); + + ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT); + bp_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT); + mono_mprotect (bp_trigger_page, mono_pagesize (), 0); } /* @@ -506,6 +553,14 @@ guint32 mono_arch_cpu_optimizazions (guint32 *exclude_mask) { guint32 opts = 0; + const char *cpu_arch = getenv ("MONO_CPU_ARCH"); + if (cpu_arch != NULL) { + thumb_supported = strstr (cpu_arch, "thumb") != NULL; + if (strncmp (cpu_arch, "armv", 4) == 0) { + v5_supported = cpu_arch [4] >= '5'; + v7_supported = cpu_arch [4] >= '7'; + } + } else { #if __APPLE__ thumb_supported = TRUE; v5_supported = TRUE; @@ -517,9 +572,10 @@ mono_arch_cpu_optimizazions (guint32 *exclude_mask) while ((line = fgets (buf, 512, file))) { if (strncmp (line, "Processor", 9) == 0) { char *ver = strstr (line, "(v"); - if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7')) { + if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7')) v5_supported = TRUE; - } + if (ver && (ver [2] == '7')) + v7_supported = TRUE; continue; } if (strncmp (line, "Features", 8) == 0) { @@ -536,12 +592,15 @@ mono_arch_cpu_optimizazions (guint32 *exclude_mask) /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/ } #endif + } /* no arm-specific optimizations yet */ *exclude_mask = 0; return opts; } +#ifndef DISABLE_JIT + static gboolean is_regsize_var (MonoType *t) { if (t->byref) @@ -605,6 +664,15 @@ GList * mono_arch_get_global_int_regs (MonoCompile *cfg) { GList *regs = NULL; + + /* + * FIXME: Interface calls might go through a static rgctx trampoline which + * sets V5, but it doesn't save it, so we need to save it ourselves, and + * avoid using it. + */ + if (cfg->flags & MONO_CFG_HAS_CALLS) + cfg->uses_rgctx_reg = TRUE; + regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1)); regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2)); regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3)); @@ -632,6 +700,8 @@ mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv) return 2; } +#endif /* #ifndef DISABLE_JIT */ + #ifndef __GNUC_PREREQ #define __GNUC_PREREQ(maj, min) (0) #endif @@ -653,7 +723,7 @@ mono_arch_flush_icache (guint8 *code, gint size) "svc 0x00000000\n" : : "r" (code), "r" (code + size), "r" (syscall) - : "r0", "r1", "r7" + : "r0", "r1", "r7", "r2" ); #else __asm __volatile ("mov r0, %0\n" @@ -666,20 +736,22 @@ mono_arch_flush_icache (guint8 *code, gint size) #endif } -enum { +typedef enum { + RegTypeNone, RegTypeGeneral, + RegTypeIRegPair, RegTypeBase, RegTypeBaseGen, RegTypeFP, RegTypeStructByVal, RegTypeStructByAddr -}; +} ArgStorage; typedef struct { gint32 offset; guint16 vtsize; /* in param area */ guint8 reg; - guint8 regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */ + ArgStorage storage; guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */ } ArgInfo; @@ -687,6 +759,7 @@ typedef struct { int nargs; guint32 stack_usage; guint32 struct_ret; + gboolean vtype_retaddr; ArgInfo ret; ArgInfo sig_cookie; ArgInfo args [1]; @@ -694,6 +767,13 @@ typedef struct { #define DEBUG(a) +#ifndef __GNUC__ +/*#define __alignof__(a) sizeof(a)*/ +#define __alignof__(type) G_STRUCT_OFFSET(struct { char c; type x; }, x) +#endif + +#define PARAM_REGS 4 + static void inline add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple) { @@ -701,36 +781,49 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple) if (*gr > ARMREG_R3) { ainfo->offset = *stack_size; ainfo->reg = ARMREG_SP; /* in the caller */ - ainfo->regtype = RegTypeBase; + ainfo->storage = RegTypeBase; *stack_size += 4; } else { + ainfo->storage = RegTypeGeneral; ainfo->reg = *gr; } } else { - if (*gr == ARMREG_R3 -#ifdef __ARM_EABI__ - && 0 +#if defined(__APPLE__) && defined(MONO_CROSS_COMPILE) + int i8_align = 4; +#else + int i8_align = __alignof__ (gint64); +#endif + +#if __ARM_EABI__ + gboolean split = i8_align == 4; +#else + gboolean split = TRUE; #endif - ) { + + if (*gr == ARMREG_R3 && split) { /* first word in r3 and the second on the stack */ ainfo->offset = *stack_size; ainfo->reg = ARMREG_SP; /* in the caller */ - ainfo->regtype = RegTypeBaseGen; + ainfo->storage = RegTypeBaseGen; *stack_size += 4; } else if (*gr >= ARMREG_R3) { #ifdef __ARM_EABI__ - *stack_size += 7; - *stack_size &= ~7; + /* darwin aligns longs to 4 byte only */ + if (i8_align == 8) { + *stack_size += 7; + *stack_size &= ~7; + } #endif ainfo->offset = *stack_size; ainfo->reg = ARMREG_SP; /* in the caller */ - ainfo->regtype = RegTypeBase; + ainfo->storage = RegTypeBase; *stack_size += 8; } else { #ifdef __ARM_EABI__ - if ((*gr) & 1) + if (i8_align == 8 && ((*gr) & 1)) (*gr) ++; #endif + ainfo->storage = RegTypeIRegPair; ainfo->reg = *gr; } (*gr) ++; @@ -739,20 +832,33 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple) } static CallInfo* -get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) +get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke) { guint i, gr; int n = sig->hasthis + sig->param_count; MonoType *simpletype; guint32 stack_size = 0; - CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n); + CallInfo *cinfo; + + if (mp) + cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n)); + else + cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n)); + cinfo->nargs = n; gr = ARMREG_R0; /* FIXME: handle returning a struct */ if (MONO_TYPE_ISSTRUCT (sig->ret)) { - add_general (&gr, &stack_size, &cinfo->ret, TRUE); - cinfo->struct_ret = ARMREG_R0; + guint32 align; + + if (is_pinvoke && mono_class_native_size (mono_class_from_mono_type (sig->ret), &align) <= sizeof (gpointer)) { + cinfo->ret.storage = RegTypeStructByVal; + } else { + add_general (&gr, &stack_size, &cinfo->ret, TRUE); + cinfo->struct_ret = ARMREG_R0; + cinfo->vtype_retaddr = TRUE; + } } n = 0; @@ -763,13 +869,13 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) DEBUG(printf("params: %d\n", sig->param_count)); for (i = 0; i < sig->param_count; ++i) { if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) { - /* Prevent implicit arguments and sig_cookie from + /* Prevent implicit arguments and sig_cookie from being passed in registers */ - gr = ARMREG_R3 + 1; - /* Emit the signature cookie just before the implicit arguments */ - add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE); - } - DEBUG(printf("param %d: ", i)); + gr = ARMREG_R3 + 1; + /* Emit the signature cookie just before the implicit arguments */ + add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE); + } + DEBUG(printf("param %d: ", i)); if (sig->params [i]->byref) { DEBUG(printf("byref\n")); add_general (&gr, &stack_size, cinfo->args + n, TRUE); @@ -813,7 +919,7 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) n++; break; case MONO_TYPE_GENERICINST: - if (!mono_type_generic_inst_is_valuetype (sig->params [i])) { + if (!mono_type_generic_inst_is_valuetype (simpletype)) { cinfo->args [n].size = sizeof (gpointer); add_general (&gr, &stack_size, cinfo->args + n, TRUE); n++; @@ -844,7 +950,7 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) align_size += (sizeof (gpointer) - 1); align_size &= ~(sizeof (gpointer) - 1); nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer); - cinfo->args [n].regtype = RegTypeStructByVal; + cinfo->args [n].storage = RegTypeStructByVal; /* FIXME: align stack_size if needed */ #ifdef __ARM_EABI__ if (align >= 8 && (gr & 1)) @@ -881,6 +987,15 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) } } + /* Handle the case where there are no implicit arguments */ + if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) { + /* Prevent implicit arguments and sig_cookie from + being passed in registers */ + gr = ARMREG_R3 + 1; + /* Emit the signature cookie just before the implicit arguments */ + add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE); + } + { simpletype = mini_type_get_underlying_type (NULL, sig->ret); switch (simpletype->type) { @@ -901,27 +1016,33 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: case MONO_TYPE_STRING: + cinfo->ret.storage = RegTypeGeneral; cinfo->ret.reg = ARMREG_R0; break; case MONO_TYPE_U8: case MONO_TYPE_I8: + cinfo->ret.storage = RegTypeIRegPair; cinfo->ret.reg = ARMREG_R0; break; case MONO_TYPE_R4: case MONO_TYPE_R8: + cinfo->ret.storage = RegTypeFP; cinfo->ret.reg = ARMREG_R0; /* FIXME: cinfo->ret.reg = ???; - cinfo->ret.regtype = RegTypeFP;*/ + cinfo->ret.storage = RegTypeFP;*/ break; case MONO_TYPE_GENERICINST: - if (!mono_type_generic_inst_is_valuetype (sig->ret)) { + if (!mono_type_generic_inst_is_valuetype (simpletype)) { + cinfo->ret.storage = RegTypeGeneral; cinfo->ret.reg = ARMREG_R0; break; } - break; + /* Fall through */ case MONO_TYPE_VALUETYPE: - break; case MONO_TYPE_TYPEDBYREF: + if (cinfo->ret.storage != RegTypeStructByVal) + cinfo->ret.storage = RegTypeStructByAddr; + break; case MONO_TYPE_VOID: break; default: @@ -937,6 +1058,7 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke) return cinfo; } +#ifndef DISABLE_JIT /* * Set var information according to the calling convention. arm version. @@ -947,9 +1069,17 @@ mono_arch_allocate_vars (MonoCompile *cfg) { MonoMethodSignature *sig; MonoMethodHeader *header; - MonoInst *inst; + MonoInst *ins; int i, offset, size, align, curinst; int frame_reg = ARMREG_FP; + CallInfo *cinfo; + guint32 ualign; + + sig = mono_method_signature (cfg->method); + + if (!cfg->arch.cinfo) + cfg->arch.cinfo = get_call_info (cfg->mempool, sig, sig->pinvoke); + cinfo = cfg->arch.cinfo; /* FIXME: this will change when we use FP as gcc does */ cfg->flags |= MONO_CFG_HAS_SPILLUP; @@ -958,7 +1088,7 @@ mono_arch_allocate_vars (MonoCompile *cfg) if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method)) cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8); - header = mono_method_get_header (cfg->method); + header = cfg->header; /* * We use the frame register also for any method that has @@ -980,12 +1110,9 @@ mono_arch_allocate_vars (MonoCompile *cfg) /* V5 is reserved for passing the vtable/rgctx/IMT method */ cfg->used_int_regs |= (1 << ARMREG_V5); - sig = mono_method_signature (cfg->method); - offset = 0; curinst = 0; if (!MONO_TYPE_ISSTRUCT (sig->ret)) { - /* FIXME: handle long and FP values */ switch (mini_type_get_underlying_type (NULL, sig->ret)->type) { case MONO_TYPE_VOID: break; @@ -1017,42 +1144,68 @@ mono_arch_allocate_vars (MonoCompile *cfg) offset += 8; /* the MonoLMF structure is stored just below the stack pointer */ - - if (sig->call_convention == MONO_CALL_VARARG) { - cfg->sig_cookie = 0; - } - if (MONO_TYPE_ISSTRUCT (sig->ret)) { - inst = cfg->vret_addr; - offset += sizeof(gpointer) - 1; - offset &= ~(sizeof(gpointer) - 1); - inst->inst_offset = offset; - inst->opcode = OP_REGOFFSET; - inst->inst_basereg = frame_reg; - if (G_UNLIKELY (cfg->verbose_level > 1)) { - printf ("vret_addr ="); - mono_print_ins (cfg->vret_addr); + if (cinfo->ret.storage == RegTypeStructByVal) { + cfg->ret->opcode = OP_REGOFFSET; + cfg->ret->inst_basereg = cfg->frame_reg; + offset += sizeof (gpointer) - 1; + offset &= ~(sizeof (gpointer) - 1); + cfg->ret->inst_offset = - offset; + } else { + ins = cfg->vret_addr; + offset += sizeof(gpointer) - 1; + offset &= ~(sizeof(gpointer) - 1); + ins->inst_offset = offset; + ins->opcode = OP_REGOFFSET; + ins->inst_basereg = frame_reg; + if (G_UNLIKELY (cfg->verbose_level > 1)) { + printf ("vret_addr ="); + mono_print_ins (cfg->vret_addr); + } } offset += sizeof(gpointer); - if (sig->call_convention == MONO_CALL_VARARG) - cfg->sig_cookie += sizeof (gpointer); + } + + /* Allocate these first so they have a small offset, OP_SEQ_POINT depends on this */ + if (cfg->arch.seq_point_info_var) { + MonoInst *ins; + + ins = cfg->arch.seq_point_info_var; + + size = 4; + align = 4; + offset += align - 1; + offset &= ~(align - 1); + ins->opcode = OP_REGOFFSET; + ins->inst_basereg = frame_reg; + ins->inst_offset = offset; + offset += size; + + ins = cfg->arch.ss_trigger_page_var; + size = 4; + align = 4; + offset += align - 1; + offset &= ~(align - 1); + ins->opcode = OP_REGOFFSET; + ins->inst_basereg = frame_reg; + ins->inst_offset = offset; + offset += size; } curinst = cfg->locals_start; for (i = curinst; i < cfg->num_varinfo; ++i) { - inst = cfg->varinfo [i]; - if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR) + ins = cfg->varinfo [i]; + if ((ins->flags & MONO_INST_IS_DEAD) || ins->opcode == OP_REGVAR || ins->opcode == OP_REGOFFSET) continue; /* inst->backend.is_pinvoke indicates native sized value types, this is used by the * pinvoke wrappers when they call functions returning structure */ - if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) { - guint32 ualign; - size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign); + if (ins->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (ins->inst_vtype) && ins->inst_vtype->type != MONO_TYPE_TYPEDBYREF) { + size = mono_class_native_size (mono_class_from_mono_type (ins->inst_vtype), &ualign); align = ualign; } else - size = mono_type_size (inst->inst_vtype, &align); + size = mono_type_size (ins->inst_vtype, &align); /* FIXME: if a structure is misaligned, our memcpy doesn't work, * since it loads/stores misaligned words, which don't do the right thing. @@ -1061,46 +1214,58 @@ mono_arch_allocate_vars (MonoCompile *cfg) align = 4; offset += align - 1; offset &= ~(align - 1); - inst->inst_offset = offset; - inst->opcode = OP_REGOFFSET; - inst->inst_basereg = frame_reg; + ins->opcode = OP_REGOFFSET; + ins->inst_offset = offset; + ins->inst_basereg = frame_reg; offset += size; //g_print ("allocating local %d to %d\n", i, inst->inst_offset); } curinst = 0; if (sig->hasthis) { - inst = cfg->args [curinst]; - if (inst->opcode != OP_REGVAR) { - inst->opcode = OP_REGOFFSET; - inst->inst_basereg = frame_reg; + ins = cfg->args [curinst]; + if (ins->opcode != OP_REGVAR) { + ins->opcode = OP_REGOFFSET; + ins->inst_basereg = frame_reg; offset += sizeof (gpointer) - 1; offset &= ~(sizeof (gpointer) - 1); - inst->inst_offset = offset; + ins->inst_offset = offset; offset += sizeof (gpointer); - if (sig->call_convention == MONO_CALL_VARARG) - cfg->sig_cookie += sizeof (gpointer); } curinst++; } + if (sig->call_convention == MONO_CALL_VARARG) { + size = 4; + align = 4; + + /* Allocate a local slot to hold the sig cookie address */ + offset += align - 1; + offset &= ~(align - 1); + cfg->sig_cookie = offset; + offset += size; + } + for (i = 0; i < sig->param_count; ++i) { - inst = cfg->args [curinst]; - if (inst->opcode != OP_REGVAR) { - inst->opcode = OP_REGOFFSET; - inst->inst_basereg = frame_reg; - size = mono_type_size (sig->params [i], &align); + ins = cfg->args [curinst]; + + if (ins->opcode != OP_REGVAR) { + ins->opcode = OP_REGOFFSET; + ins->inst_basereg = frame_reg; + size = mini_type_stack_size_full (NULL, sig->params [i], &ualign, sig->pinvoke); + align = ualign; /* FIXME: if a structure is misaligned, our memcpy doesn't work, * since it loads/stores misaligned words, which don't do the right thing. */ if (align < 4 && size >= 4) align = 4; + /* The code in the prolog () stores words when storing vtypes received in a register */ + if (MONO_TYPE_ISSTRUCT (sig->params [i])) + align = 4; offset += align - 1; offset &= ~(align - 1); - inst->inst_offset = offset; + ins->inst_offset = offset; offset += size; - if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos)) - cfg->sig_cookie += size; } curinst++; } @@ -1117,17 +1282,118 @@ void mono_arch_create_vars (MonoCompile *cfg) { MonoMethodSignature *sig; + CallInfo *cinfo; sig = mono_method_signature (cfg->method); - if (MONO_TYPE_ISSTRUCT (sig->ret)) { + if (!cfg->arch.cinfo) + cfg->arch.cinfo = get_call_info (cfg->mempool, sig, sig->pinvoke); + cinfo = cfg->arch.cinfo; + + if (cinfo->ret.storage == RegTypeStructByVal) + cfg->ret_var_is_local = TRUE; + + if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage != RegTypeStructByVal) { cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG); if (G_UNLIKELY (cfg->verbose_level > 1)) { printf ("vret_addr = "); mono_print_ins (cfg->vret_addr); } } + + if (cfg->gen_seq_points && cfg->compile_aot) { + MonoInst *ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + ins->flags |= MONO_INST_VOLATILE; + cfg->arch.seq_point_info_var = ins; + + /* Allocate a separate variable for this to save 1 load per seq point */ + ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + ins->flags |= MONO_INST_VOLATILE; + cfg->arch.ss_trigger_page_var = ins; + } +} + +static void +emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo) +{ + MonoMethodSignature *tmp_sig; + MonoInst *sig_arg; + + if (call->tail_call) + NOT_IMPLEMENTED; + + /* FIXME: Add support for signature tokens to AOT */ + cfg->disable_aot = TRUE; + + g_assert (cinfo->sig_cookie.storage == RegTypeBase); + + /* + * mono_ArgIterator_Setup assumes the signature cookie is + * passed first and all the arguments which were before it are + * passed on the stack after the signature. So compensate by + * passing a different signature. + */ + tmp_sig = mono_metadata_signature_dup (call->signature); + tmp_sig->param_count -= call->signature->sentinelpos; + tmp_sig->sentinelpos = 0; + memcpy (tmp_sig->params, call->signature->params + call->signature->sentinelpos, tmp_sig->param_count * sizeof (MonoType*)); + + MONO_INST_NEW (cfg, sig_arg, OP_ICONST); + sig_arg->dreg = mono_alloc_ireg (cfg); + sig_arg->inst_p0 = tmp_sig; + MONO_ADD_INS (cfg->cbb, sig_arg); + + MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, cinfo->sig_cookie.offset, sig_arg->dreg); +} + +#ifdef ENABLE_LLVM +LLVMCallInfo* +mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) +{ + int i, n; + CallInfo *cinfo; + ArgInfo *ainfo; + LLVMCallInfo *linfo; + + n = sig->param_count + sig->hasthis; + + cinfo = get_call_info (cfg->mempool, sig, sig->pinvoke); + + linfo = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMCallInfo) + (sizeof (LLVMArgInfo) * n)); + + /* + * LLVM always uses the native ABI while we use our own ABI, the + * only difference is the handling of vtypes: + * - we only pass/receive them in registers in some cases, and only + * in 1 or 2 integer registers. + */ + if (cinfo->ret.storage != RegTypeGeneral && cinfo->ret.storage != RegTypeNone && cinfo->ret.storage != RegTypeFP && cinfo->ret.storage != RegTypeIRegPair) { + cfg->exception_message = g_strdup ("unknown ret conv"); + cfg->disable_llvm = TRUE; + return linfo; + } + + for (i = 0; i < n; ++i) { + ainfo = cinfo->args + i; + + linfo->args [i].storage = LLVMArgNone; + + switch (ainfo->storage) { + case RegTypeGeneral: + case RegTypeIRegPair: + case RegTypeBase: + linfo->args [i].storage = LLVMArgInIReg; + break; + default: + cfg->exception_message = g_strdup_printf ("ainfo->storage (%d)", ainfo->storage); + cfg->disable_llvm = TRUE; + break; + } + } + + return linfo; } +#endif void mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) @@ -1140,7 +1406,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) sig = call->signature; n = sig->param_count + sig->hasthis; - cinfo = get_call_info (sig, sig->pinvoke); + cinfo = get_call_info (NULL, sig, sig->pinvoke); for (i = 0; i < n; ++i) { ArgInfo *ainfo = cinfo->args + i; @@ -1153,14 +1419,15 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) t = mini_type_get_underlying_type (NULL, t); if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) { - /* FIXME: */ - NOT_IMPLEMENTED; + /* Emit the signature cookie just before the implicit arguments */ + emit_sig_cookie (cfg, call, cinfo); } in = call->args [i]; - switch (ainfo->regtype) { + switch (ainfo->storage) { case RegTypeGeneral: + case RegTypeIRegPair: if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) { MONO_INST_NEW (cfg, ins, OP_MOVE); ins->dreg = mono_alloc_ireg (cfg); @@ -1311,15 +1578,24 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) } } + /* Handle the case where there are no implicit arguments */ + if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (n == sig->sentinelpos)) + emit_sig_cookie (cfg, call, cinfo); + if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) { MonoInst *vtarg; - MONO_INST_NEW (cfg, vtarg, OP_MOVE); - vtarg->sreg1 = call->vret_var->dreg; - vtarg->dreg = mono_alloc_preg (cfg); - MONO_ADD_INS (cfg->cbb, vtarg); + if (cinfo->ret.storage == RegTypeStructByVal) { + /* The JIT will transform this into a normal call */ + call->vret_in_reg = TRUE; + } else { + MONO_INST_NEW (cfg, vtarg, OP_MOVE); + vtarg->sreg1 = call->vret_var->dreg; + vtarg->dreg = mono_alloc_preg (cfg); + MONO_ADD_INS (cfg->cbb, vtarg); - mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE); + mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE); + } } call->stack_usage = cinfo->stack_usage; @@ -1357,10 +1633,14 @@ mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val) if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) { MonoInst *ins; - MONO_INST_NEW (cfg, ins, OP_SETLRET); - ins->sreg1 = val->dreg + 1; - ins->sreg2 = val->dreg + 2; - MONO_ADD_INS (cfg->cbb, ins); + if (COMPILE_LLVM (cfg)) { + MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg); + } else { + MONO_INST_NEW (cfg, ins, OP_SETLRET); + ins->sreg1 = val->dreg + 1; + ins->sreg2 = val->dreg + 2; + MONO_ADD_INS (cfg->cbb, ins); + } return; } #ifdef MONO_ARCH_SOFT_FLOAT @@ -1395,16 +1675,322 @@ mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val) } #endif } - - /* FIXME: */ - MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg); + + /* FIXME: */ + MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg); +} + +#endif /* #ifndef DISABLE_JIT */ + +gboolean +mono_arch_is_inst_imm (gint64 imm) +{ + return TRUE; +} + +#define DYN_CALL_STACK_ARGS 6 + +typedef struct { + MonoMethodSignature *sig; + CallInfo *cinfo; +} ArchDynCallInfo; + +typedef struct { + mgreg_t regs [PARAM_REGS + DYN_CALL_STACK_ARGS]; + mgreg_t res, res2; + guint8 *ret; +} DynCallArgs; + +static gboolean +dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig) +{ + int i; + + if (sig->hasthis + sig->param_count > PARAM_REGS + DYN_CALL_STACK_ARGS) + return FALSE; + + switch (cinfo->ret.storage) { + case RegTypeNone: + case RegTypeGeneral: + case RegTypeIRegPair: + case RegTypeStructByAddr: + break; + case RegTypeFP: +#ifdef ARM_FPU_FPA + return FALSE; +#elif defined(ARM_FPU_VFP) + break; +#else + return FALSE; +#endif + default: + return FALSE; + } + + for (i = 0; i < cinfo->nargs; ++i) { + switch (cinfo->args [i].storage) { + case RegTypeGeneral: + break; + case RegTypeIRegPair: + break; + case RegTypeBase: + if (cinfo->args [i].offset >= (DYN_CALL_STACK_ARGS * sizeof (gpointer))) + return FALSE; + break; + case RegTypeStructByVal: + if (cinfo->args [i].reg + cinfo->args [i].vtsize >= PARAM_REGS + DYN_CALL_STACK_ARGS) + return FALSE; + break; + default: + return FALSE; + } + } + + // FIXME: Can't use cinfo only as it doesn't contain info about I8/float */ + for (i = 0; i < sig->param_count; ++i) { + MonoType *t = sig->params [i]; + + if (t->byref) + continue; + + switch (t->type) { + case MONO_TYPE_R4: + case MONO_TYPE_R8: +#ifdef MONO_ARCH_SOFT_FLOAT + return FALSE; +#else + break; +#endif + /* + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return FALSE; + */ + default: + break; + } + } + + return TRUE; +} + +MonoDynCallInfo* +mono_arch_dyn_call_prepare (MonoMethodSignature *sig) +{ + ArchDynCallInfo *info; + CallInfo *cinfo; + + cinfo = get_call_info (NULL, sig, FALSE); + + if (!dyn_call_supported (cinfo, sig)) { + g_free (cinfo); + return NULL; + } + + info = g_new0 (ArchDynCallInfo, 1); + // FIXME: Preprocess the info to speed up start_dyn_call () + info->sig = sig; + info->cinfo = cinfo; + + return (MonoDynCallInfo*)info; +} + +void +mono_arch_dyn_call_free (MonoDynCallInfo *info) +{ + ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info; + + g_free (ainfo->cinfo); + g_free (ainfo); +} + +void +mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len) +{ + ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info; + DynCallArgs *p = (DynCallArgs*)buf; + int arg_index, greg, i, j; + MonoMethodSignature *sig = dinfo->sig; + + g_assert (buf_len >= sizeof (DynCallArgs)); + + p->res = 0; + p->ret = ret; + + arg_index = 0; + greg = 0; + + if (dinfo->cinfo->vtype_retaddr) + p->regs [greg ++] = (mgreg_t)ret; + + if (sig->hasthis) + p->regs [greg ++] = (mgreg_t)*(args [arg_index ++]); + + for (i = 0; i < sig->param_count; i++) { + MonoType *t = mono_type_get_underlying_type (sig->params [i]); + gpointer *arg = args [arg_index ++]; + ArgInfo *ainfo = &dinfo->cinfo->args [i + sig->hasthis]; + int slot = -1; + + if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair || ainfo->storage == RegTypeStructByVal) + slot = ainfo->reg; + else if (ainfo->storage == RegTypeBase) + slot = PARAM_REGS + (ainfo->offset / 4); + else + g_assert_not_reached (); + + if (t->byref) { + p->regs [slot] = (mgreg_t)*arg; + continue; + } + + switch (t->type) { + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + case MONO_TYPE_PTR: + case MONO_TYPE_I: + case MONO_TYPE_U: + p->regs [slot] = (mgreg_t)*arg; + break; + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + p->regs [slot] = *(guint8*)arg; + break; + case MONO_TYPE_I1: + p->regs [slot] = *(gint8*)arg; + break; + case MONO_TYPE_I2: + p->regs [slot] = *(gint16*)arg; + break; + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + p->regs [slot] = *(guint16*)arg; + break; + case MONO_TYPE_I4: + p->regs [slot] = *(gint32*)arg; + break; + case MONO_TYPE_U4: + p->regs [slot] = *(guint32*)arg; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + p->regs [slot ++] = (mgreg_t)arg [0]; + p->regs [slot] = (mgreg_t)arg [1]; + break; + case MONO_TYPE_R4: + p->regs [slot] = *(mgreg_t*)arg; + break; + case MONO_TYPE_R8: + p->regs [slot ++] = (mgreg_t)arg [0]; + p->regs [slot] = (mgreg_t)arg [1]; + break; + case MONO_TYPE_GENERICINST: + if (MONO_TYPE_IS_REFERENCE (t)) { + p->regs [slot] = (mgreg_t)*arg; + break; + } else { + /* Fall though */ + } + case MONO_TYPE_VALUETYPE: + g_assert (ainfo->storage == RegTypeStructByVal); + + if (ainfo->size == 0) + slot = PARAM_REGS + (ainfo->offset / 4); + else + slot = ainfo->reg; + + for (j = 0; j < ainfo->size + ainfo->vtsize; ++j) + p->regs [slot ++] = ((mgreg_t*)arg) [j]; + break; + default: + g_assert_not_reached (); + } + } +} + +void +mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf) +{ + ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info; + MonoMethodSignature *sig = ((ArchDynCallInfo*)info)->sig; + guint8 *ret = ((DynCallArgs*)buf)->ret; + mgreg_t res = ((DynCallArgs*)buf)->res; + mgreg_t res2 = ((DynCallArgs*)buf)->res2; + + switch (mono_type_get_underlying_type (sig->ret)->type) { + case MONO_TYPE_VOID: + *(gpointer*)ret = NULL; + break; + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + *(gpointer*)ret = (gpointer)res; + break; + case MONO_TYPE_I1: + *(gint8*)ret = res; + break; + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + *(guint8*)ret = res; + break; + case MONO_TYPE_I2: + *(gint16*)ret = res; + break; + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + *(guint16*)ret = res; + break; + case MONO_TYPE_I4: + *(gint32*)ret = res; + break; + case MONO_TYPE_U4: + *(guint32*)ret = res; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + /* This handles endianness as well */ + ((gint32*)ret) [0] = res; + ((gint32*)ret) [1] = res2; + break; + case MONO_TYPE_GENERICINST: + if (MONO_TYPE_IS_REFERENCE (sig->ret)) { + *(gpointer*)ret = (gpointer)res; + break; + } else { + /* Fall though */ + } + case MONO_TYPE_VALUETYPE: + g_assert (ainfo->cinfo->vtype_retaddr); + /* Nothing to do */ + break; +#if defined(ARM_FPU_VFP) + case MONO_TYPE_R4: + *(float*)ret = *(float*)&res; + break; + case MONO_TYPE_R8: { + mgreg_t regs [2]; + + regs [0] = res; + regs [1] = res2; + + *(double*)ret = *(double*)®s; + break; + } +#endif + default: + g_assert_not_reached (); + } } -gboolean -mono_arch_is_inst_imm (gint64 imm) -{ - return TRUE; -} +#ifndef DISABLE_JIT /* * Allow tracing to work with this interface (with an optional argument) @@ -1955,8 +2541,13 @@ loop_start: gboolean swap = FALSE; int reg; + if (!ins->next) { + /* Optimized away */ + NULLIFY_INS (ins); + break; + } + /* Some fp compares require swapped operands */ - g_assert (ins->next); switch (ins->next->opcode) { case OP_FBGT: ins->next->opcode = OP_FBLT; @@ -1996,7 +2587,6 @@ void mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins) { MonoInst *ins; - int vreg; if (long_ins->opcode == OP_LNEG) { ins = long_ins; @@ -2038,6 +2628,8 @@ emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, return code; } +#endif /* #ifndef DISABLE_JIT */ + typedef struct { guchar *code; const guchar *target; @@ -2107,10 +2699,13 @@ search_thunk_slot (void *data, int csize, int bsize, void *user_data) { } static void -handle_thunk (int absolute, guchar *code, const guchar *target) { - MonoDomain *domain = mono_domain_get (); +handle_thunk (MonoDomain *domain, int absolute, guchar *code, const guchar *target) +{ PatchData pdata; + if (!domain) + domain = mono_domain_get (); + pdata.code = code; pdata.target = target; pdata.absolute = absolute; @@ -2131,8 +2726,8 @@ handle_thunk (int absolute, guchar *code, const guchar *target) { g_assert (pdata.found == 1); } -void -arm_patch (guchar *code, const guchar *target) +static void +arm_patch_general (MonoDomain *domain, guchar *code, const guchar *target) { guint32 *code32 = (void*)code; guint32 ins = *code32; @@ -2178,7 +2773,7 @@ arm_patch (guchar *code, const guchar *target) } } - handle_thunk (TRUE, code, target); + handle_thunk (domain, TRUE, code, target); return; } @@ -2278,6 +2873,12 @@ arm_patch (guchar *code, const guchar *target) // g_print ("patched with 0x%08x\n", ins); } +void +arm_patch (guchar *code, const guchar *target) +{ + arm_patch_general (NULL, code, target); +} + /* * Return the >= 0 uimm8 value if val can be represented with a byte + rotation * (with the rotation amount in *rot_amount. rot_amount is already adjusted @@ -2319,6 +2920,12 @@ mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val) } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) { ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount); } else { + if (v7_supported) { + ARM_MOVW_REG_IMM (code, dreg, val & 0xffff); + if (val >> 16) + ARM_MOVT_REG_IMM (code, dreg, (val >> 16) & 0xffff); + return code; + } if (val & 0xFF) { ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF)); if (val & 0xFF00) { @@ -2349,6 +2956,14 @@ mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val) return code; } +gboolean +mono_arm_thumb_supported (void) +{ + return thumb_supported; +} + +#ifndef DISABLE_JIT + /* * emit_load_volatile_arguments: * @@ -2372,7 +2987,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code) pos = 0; - cinfo = get_call_info (sig, sig->pinvoke); + cinfo = get_call_info (NULL, sig, sig->pinvoke); if (MONO_TYPE_ISSTRUCT (sig->ret)) { ArgInfo *ainfo = &cinfo->ret; @@ -2385,13 +3000,13 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code) inst = cfg->args [pos]; if (cfg->verbose_level > 2) - g_print ("Loading argument %d (type: %d)\n", i, ainfo->regtype); + g_print ("Loading argument %d (type: %d)\n", i, ainfo->storage); if (inst->opcode == OP_REGVAR) { - if (ainfo->regtype == RegTypeGeneral) + if (ainfo->storage == RegTypeGeneral) ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg); - else if (ainfo->regtype == RegTypeFP) { + else if (ainfo->storage == RegTypeFP) { g_assert_not_reached (); - } else if (ainfo->regtype == RegTypeBase) { + } else if (ainfo->storage == RegTypeBase) { // FIXME: NOT_IMPLEMENTED; /* @@ -2405,7 +3020,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code) } else g_assert_not_reached (); } else { - if (ainfo->regtype == RegTypeGeneral) { + if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair) { switch (ainfo->size) { case 1: case 2: @@ -2427,14 +3042,14 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code) } break; } - } else if (ainfo->regtype == RegTypeBaseGen) { + } else if (ainfo->storage == RegTypeBaseGen) { // FIXME: NOT_IMPLEMENTED; - } else if (ainfo->regtype == RegTypeBase) { + } else if (ainfo->storage == RegTypeBase) { /* Nothing to do */ - } else if (ainfo->regtype == RegTypeFP) { + } else if (ainfo->storage == RegTypeFP) { g_assert_not_reached (); - } else if (ainfo->regtype == RegTypeStructByVal) { + } else if (ainfo->storage == RegTypeStructByVal) { int doffset = inst->inst_offset; int soffset = 0; int cur_reg; @@ -2454,7 +3069,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code) if (ainfo->vtsize) // FIXME: NOT_IMPLEMENTED; - } else if (ainfo->regtype == RegTypeStructByAddr) { + } else if (ainfo->storage == RegTypeStructByAddr) { } else { // FIXME: NOT_IMPLEMENTED; @@ -2468,8 +3083,6 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code) return code; } -#ifndef DISABLE_JIT - void mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) { @@ -2680,6 +3293,90 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_NOT_REACHED: case OP_NOT_NULL: break; + case OP_SEQ_POINT: { + int i; + MonoInst *info_var = cfg->arch.seq_point_info_var; + MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var; + MonoInst *var; + int dreg = ARMREG_LR; + + /* + * For AOT, we use one got slot per method, which will point to a + * SeqPointInfo structure, containing all the information required + * by the code below. + */ + if (cfg->compile_aot) { + g_assert (info_var); + g_assert (info_var->opcode == OP_REGOFFSET); + g_assert (arm_is_imm12 (info_var->inst_offset)); + } + + /* + * 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. + */ + g_assert (((guint64)(gsize)ss_trigger_page >> 32) == 0); + + if (ins->flags & MONO_INST_SINGLE_STEP_LOC) { + if (cfg->compile_aot) { + /* Load the trigger page addr from the variable initialized in the prolog */ + var = ss_trigger_page_var; + g_assert (var); + g_assert (var->opcode == OP_REGOFFSET); + g_assert (arm_is_imm12 (var->inst_offset)); + ARM_LDR_IMM (code, dreg, var->inst_basereg, var->inst_offset); + } else { + ARM_LDR_IMM (code, dreg, ARMREG_PC, 0); + ARM_B (code, 0); + *(int*)code = (int)ss_trigger_page; + code += 4; + } + ARM_LDR_IMM (code, dreg, dreg, 0); + } + + mono_add_seq_point (cfg, bb, ins, code - cfg->native_code); + + if (cfg->compile_aot) { + guint32 offset = code - cfg->native_code; + guint32 val; + + ARM_LDR_IMM (code, dreg, info_var->inst_basereg, info_var->inst_offset); + /* Add the offset */ + val = ((offset / 4) * sizeof (guint8*)) + G_STRUCT_OFFSET (SeqPointInfo, bp_addrs); + ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF), 0); + /* + * Have to emit nops to keep the difference between the offset + * stored in seq_points and breakpoint instruction constant, + * mono_arch_get_ip_for_breakpoint () depends on this. + */ + if (val & 0xFF00) + ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24); + else + ARM_NOP (code); + if (val & 0xFF0000) + ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16); + else + ARM_NOP (code); + g_assert (!(val & 0xFF000000)); + /* Load the info->bp_addrs [offset], which is either 0 or the address of a trigger page */ + ARM_LDR_IMM (code, dreg, dreg, 0); + + /* What is faster, a branch or a load ? */ + ARM_CMP_REG_IMM (code, dreg, 0, 0); + /* The breakpoint instruction */ + ARM_LDR_IMM_COND (code, dreg, dreg, 0, ARMCOND_NE); + } else { + /* + * A placeholder for a possible breakpoint inserted by + * mono_arch_set_breakpoint (). + */ + for (i = 0; i < 4; ++i) + ARM_NOP (code); + } + break; + } case OP_ADDCC: case OP_IADDCC: ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2); @@ -2940,15 +3637,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0); break; case OP_ARGLIST: { -#if ARM_PORT - if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) { - ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage); - } else { - ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage); - ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11); - } - ppc_stw (code, ppc_r11, 0, ins->sreg1); -#endif + g_assert (cfg->sig_cookie < 128); + ARM_LDR_IMM (code, ARMREG_IP, cfg->frame_reg, cfg->sig_cookie); + ARM_STR_IMM (code, ARMREG_IP, ins->sreg1, 0); break; } case OP_FCALL: @@ -3032,6 +3723,42 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste); break; } + case OP_DYN_CALL: { + int i; + MonoInst *var = cfg->dyn_call_var; + + g_assert (var->opcode == OP_REGOFFSET); + g_assert (arm_is_imm12 (var->inst_offset)); + + /* lr = args buffer filled by mono_arch_get_dyn_call_args () */ + ARM_MOV_REG_REG( code, ARMREG_LR, ins->sreg1); + /* ip = ftn */ + ARM_MOV_REG_REG( code, ARMREG_IP, ins->sreg2); + + /* Save args buffer */ + ARM_STR_IMM (code, ARMREG_LR, var->inst_basereg, var->inst_offset); + + /* Set stack slots using R0 as scratch reg */ + /* MONO_ARCH_DYN_CALL_PARAM_AREA gives the size of stack space available */ + for (i = 0; i < DYN_CALL_STACK_ARGS; ++i) { + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, (PARAM_REGS + i) * sizeof (gpointer)); + ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, i * sizeof (gpointer)); + } + + /* Set argument registers */ + for (i = 0; i < PARAM_REGS; ++i) + ARM_LDR_IMM (code, i, ARMREG_LR, i * sizeof (gpointer)); + + /* Make the call */ + ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC); + ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP); + + /* Save result */ + ARM_LDR_IMM (code, ARMREG_IP, var->inst_basereg, var->inst_offset); + ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, G_STRUCT_OFFSET (DynCallArgs, res)); + ARM_STR_IMM (code, ARMREG_R1, ARMREG_IP, G_STRUCT_OFFSET (DynCallArgs, res2)); + break; + } case OP_THROW: { if (ins->sreg1 != ARMREG_R0) ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1); @@ -3090,6 +3817,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_CALL_HANDLER: mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb); ARM_BL (code, 0); + mono_cfg_add_try_hole (cfg, ins->inst_eh_block, code, bb); break; case OP_LABEL: ins->inst_c0 = code - cfg->native_code; @@ -3412,9 +4140,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) g_assert_not_reached (); /* Implemented as helper calls */ break; - case OP_LCONV_TO_OVF_I: case OP_LCONV_TO_OVF_I4_2: { - guint32 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive; + guint8 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive; /* * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000 */ @@ -3639,9 +4366,11 @@ mono_arch_register_lowlevel_calls (void) mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE); mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE); +#ifndef MONO_CROSS_COMPILE #ifdef HAVE_AEABI_READ_TP mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE); #endif +#endif } #define patch_lis_ori(ip,val) do {\ @@ -3724,10 +4453,12 @@ mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, Mono default: break; } - arm_patch (ip, target); + arm_patch_general (domain, ip, target); } } +#ifndef DISABLE_JIT + /* * Stack frame layout: * @@ -3861,26 +4592,39 @@ mono_arch_emit_prolog (MonoCompile *cfg) /* load arguments allocated to register from the stack */ pos = 0; - cinfo = get_call_info (sig, sig->pinvoke); + cinfo = get_call_info (NULL, sig, sig->pinvoke); - if (MONO_TYPE_ISSTRUCT (sig->ret)) { + if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage != RegTypeStructByVal) { ArgInfo *ainfo = &cinfo->ret; inst = cfg->vret_addr; g_assert (arm_is_imm12 (inst->inst_offset)); ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset); } + + if (sig->call_convention == MONO_CALL_VARARG) { + ArgInfo *cookie = &cinfo->sig_cookie; + + /* Save the sig cookie address */ + g_assert (cookie->storage == RegTypeBase); + + g_assert (arm_is_imm12 (prev_sp_offset + cookie->offset)); + g_assert (arm_is_imm12 (cfg->sig_cookie)); + ARM_ADD_REG_IMM8 (code, ARMREG_IP, cfg->frame_reg, prev_sp_offset + cookie->offset); + ARM_STR_IMM (code, ARMREG_IP, cfg->frame_reg, cfg->sig_cookie); + } + for (i = 0; i < sig->param_count + sig->hasthis; ++i) { ArgInfo *ainfo = cinfo->args + i; inst = cfg->args [pos]; if (cfg->verbose_level > 2) - g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype); + g_print ("Saving argument %d (type: %d)\n", i, ainfo->storage); if (inst->opcode == OP_REGVAR) { - if (ainfo->regtype == RegTypeGeneral) + if (ainfo->storage == RegTypeGeneral) ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg); - else if (ainfo->regtype == RegTypeFP) { + else if (ainfo->storage == RegTypeFP) { g_assert_not_reached (); - } else if (ainfo->regtype == RegTypeBase) { + } else if (ainfo->storage == RegTypeBase) { if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) { ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset)); } else { @@ -3894,7 +4638,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg)); } else { /* the argument should be put on the stack: FIXME handle size != word */ - if (ainfo->regtype == RegTypeGeneral) { + if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair) { switch (ainfo->size) { case 1: if (arm_is_imm12 (inst->inst_offset)) @@ -3927,13 +4671,13 @@ mono_arch_emit_prolog (MonoCompile *cfg) } break; } - } else if (ainfo->regtype == RegTypeBaseGen) { + } else if (ainfo->storage == RegTypeBaseGen) { g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset)); g_assert (arm_is_imm12 (inst->inst_offset)); ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset)); ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4); ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset); - } else if (ainfo->regtype == RegTypeBase) { + } else if (ainfo->storage == RegTypeBase) { if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) { ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset)); } else { @@ -3987,9 +4731,9 @@ mono_arch_emit_prolog (MonoCompile *cfg) } break; } - } else if (ainfo->regtype == RegTypeFP) { + } else if (ainfo->storage == RegTypeFP) { g_assert_not_reached (); - } else if (ainfo->regtype == RegTypeStructByVal) { + } else if (ainfo->storage == RegTypeStructByVal) { int doffset = inst->inst_offset; int soffset = 0; int cur_reg; @@ -4010,7 +4754,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset); code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset); } - } else if (ainfo->regtype == RegTypeStructByAddr) { + } else if (ainfo->storage == RegTypeStructByAddr) { g_assert_not_reached (); /* FIXME: handle overrun! with struct sizes not multiple of 4 */ code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0); @@ -4079,6 +4823,44 @@ mono_arch_emit_prolog (MonoCompile *cfg) if (tracing) code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE); + if (cfg->arch.seq_point_info_var) { + MonoInst *ins = cfg->arch.seq_point_info_var; + + /* Initialize the variable from a GOT slot */ + mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_SEQ_POINT_INFO, cfg->method); + ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0); + ARM_B (code, 0); + *(gpointer*)code = NULL; + code += 4; + ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0); + + g_assert (ins->opcode == OP_REGOFFSET); + + if (arm_is_imm12 (ins->inst_offset)) { + ARM_STR_IMM (code, ARMREG_R0, ins->inst_basereg, ins->inst_offset); + } else { + code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset); + ARM_STR_REG_REG (code, ARMREG_R0, ins->inst_basereg, ARMREG_LR); + } + } + + /* Initialize ss_trigger_page_var */ + { + MonoInst *info_var = cfg->arch.seq_point_info_var; + MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var; + int dreg = ARMREG_LR; + + if (info_var) { + g_assert (info_var->opcode == OP_REGOFFSET); + g_assert (arm_is_imm12 (info_var->inst_offset)); + + ARM_LDR_IMM (code, dreg, info_var->inst_basereg, info_var->inst_offset); + /* Load the trigger page addr */ + ARM_LDR_IMM (code, dreg, dreg, G_STRUCT_OFFSET (SeqPointInfo, ss_trigger_page)); + ARM_STR_IMM (code, dreg, ss_trigger_page_var->inst_basereg, ss_trigger_page_var->inst_offset); + } + } + cfg->code_len = code - cfg->native_code; g_assert (cfg->code_len < cfg->code_size); g_free (cinfo); @@ -4093,6 +4875,7 @@ mono_arch_emit_epilog (MonoCompile *cfg) int pos, i, rot_amount; int max_epilog_size = 16 + 20*4; guint8 *code; + CallInfo *cinfo; if (cfg->method->save_lmf) max_epilog_size += 128; @@ -4119,6 +4902,19 @@ mono_arch_emit_epilog (MonoCompile *cfg) } pos = 0; + /* Load returned vtypes into registers if needed */ + cinfo = cfg->arch.cinfo; + if (cinfo->ret.storage == RegTypeStructByVal) { + MonoInst *ins = cfg->ret; + + if (arm_is_imm12 (ins->inst_offset)) { + ARM_LDR_IMM (code, ARMREG_R0, ins->inst_basereg, ins->inst_offset); + } else { + code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset); + ARM_LDR_REG_REG (code, ARMREG_R0, ins->inst_basereg, ARMREG_LR); + } + } + if (method->save_lmf) { int lmf_offset; /* all but r0-r3, sp and pc */ @@ -4253,6 +5049,8 @@ mono_arch_emit_exceptions (MonoCompile *cfg) } +#endif /* #ifndef DISABLE_JIT */ + static gboolean tls_offset_inited = FALSE; void @@ -4290,12 +5088,6 @@ mono_arch_get_domain_intrinsic (MonoCompile* cfg) return mono_get_domain_intrinsic (cfg); } -MonoInst* -mono_arch_get_thread_intrinsic (MonoCompile* cfg) -{ - return mono_get_thread_intrinsic (cfg); -} - guint32 mono_arch_get_patch_offset (guint8 *code) { @@ -4310,6 +5102,8 @@ mono_arch_flush_register_windows (void) #ifdef MONO_ARCH_HAVE_IMT +#ifndef DISABLE_JIT + void mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg) { @@ -4353,6 +5147,8 @@ mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt } } +#endif /* DISABLE_JIT */ + MonoMethod* mono_arch_find_imt_method (mgreg_t *regs, guint8 *code) { @@ -4370,12 +5166,6 @@ mono_arch_find_imt_method (mgreg_t *regs, guint8 *code) return (MonoMethod*) code_ptr [1]; } -MonoObject* -mono_arch_find_this_argument (mgreg_t *regs, MonoMethod *method, MonoGenericSharingContext *gsctx) -{ - return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), regs, NULL); -} - MonoVTable* mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code) { @@ -4586,14 +5376,223 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI gpointer mono_arch_context_get_int_reg (MonoContext *ctx, int reg) { - if (reg >= 4 && reg <= 11) - return (gpointer)ctx->regs [reg - 4]; - else if (reg == ARMREG_IP) - return (gpointer)ctx->regs [8]; - else if (reg == ARMREG_LR) - return (gpointer)ctx->regs [9]; - else { - g_assert_not_reached (); - return NULL; + if (reg == ARMREG_SP) + return (gpointer)ctx->esp; + else + return (gpointer)ctx->regs [reg]; +} + +/* + * 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; + guint32 native_offset = ip - (guint8*)ji->code_start; + + if (ji->from_aot) { + SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start); + + g_assert (native_offset % 4 == 0); + g_assert (info->bp_addrs [native_offset / 4] == 0); + info->bp_addrs [native_offset / 4] = bp_trigger_page; + } else { + int dreg = ARMREG_LR; + + /* Read from another trigger page */ + ARM_LDR_IMM (code, dreg, ARMREG_PC, 0); + ARM_B (code, 0); + *(int*)code = (int)bp_trigger_page; + code += 4; + ARM_LDR_IMM (code, dreg, dreg, 0); + + mono_arch_flush_icache (code - 16, 16); + +#if 0 + /* This is currently implemented by emitting an SWI instruction, which + * qemu/linux seems to convert to a SIGILL. + */ + *(int*)code = (0xef << 24) | 8; + code += 4; + mono_arch_flush_icache (code - 4, 4); +#endif + } +} + +/* + * mono_arch_clear_breakpoint: + * + * Clear the breakpoint at IP. + */ +void +mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip) +{ + guint8 *code = ip; + int i; + + if (ji->from_aot) { + guint32 native_offset = ip - (guint8*)ji->code_start; + SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start); + + g_assert (native_offset % 4 == 0); + g_assert (info->bp_addrs [native_offset / 4] == bp_trigger_page); + info->bp_addrs [native_offset / 4] = 0; + } else { + for (i = 0; i < 4; ++i) + ARM_NOP (code); + + mono_arch_flush_icache (ip, code - ip); + } +} + +/* + * 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); +} + +#if __APPLE__ +#define DBG_SIGNAL SIGBUS +#else +#define DBG_SIGNAL SIGSEGV +#endif + +/* + * 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) +{ + siginfo_t *sinfo = info; + + /* Sometimes the address is off by 4 */ + if (sinfo->si_addr >= ss_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)ss_trigger_page + 128) + return TRUE; + else + return FALSE; +} + +/* + * mono_arch_is_breakpoint_event: + * + * Return whenever the machine state in SIGCTX corresponds to a breakpoint event. + */ +gboolean +mono_arch_is_breakpoint_event (void *info, void *sigctx) +{ + siginfo_t *sinfo = info; + + if (sinfo->si_signo == DBG_SIGNAL) { + /* Sometimes the address is off by 4 */ + if (sinfo->si_addr >= bp_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)bp_trigger_page + 128) + return TRUE; + else + return FALSE; + } else { + return FALSE; + } +} + +guint8* +mono_arch_get_ip_for_breakpoint (MonoJitInfo *ji, MonoContext *ctx) +{ + guint8 *ip = MONO_CONTEXT_GET_IP (ctx); + + if (ji->from_aot) + ip -= 6 * 4; + else + ip -= 12; + + return ip; +} + +guint8* +mono_arch_get_ip_for_single_step (MonoJitInfo *ji, MonoContext *ctx) +{ + guint8 *ip = MONO_CONTEXT_GET_IP (ctx); + + ip += 4; + + 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) + 4); +} + +/* + * 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) + 4); +} + +/* + * mono_arch_get_seq_point_info: + * + * See mini-amd64.c for docs. + */ +gpointer +mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code) +{ + SeqPointInfo *info; + MonoJitInfo *ji; + + // FIXME: Add a free function + + mono_domain_lock (domain); + info = g_hash_table_lookup (domain_jit_info (domain)->arch_seq_points, + code); + mono_domain_unlock (domain); + + if (!info) { + ji = mono_jit_info_table_find (domain, (char*)code); + g_assert (ji); + + info = g_malloc0 (sizeof (SeqPointInfo) + ji->code_size); + + info->ss_trigger_page = ss_trigger_page; + info->bp_trigger_page = bp_trigger_page; + + mono_domain_lock (domain); + g_hash_table_insert (domain_jit_info (domain)->arch_seq_points, + code, info); + mono_domain_unlock (domain); } + + return info; }