#include <mono/metadata/appdomain.h>
#include <mono/metadata/debug-helpers.h>
+#include <mono/utils/mono-mmap.h>
#include "mini-arm.h"
#include "cpu-arm.h"
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
#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)
return "unknown";
}
+#ifndef DISABLE_JIT
+
static guint8*
emit_big_add (guint8 *code, int dreg, int sreg, int imm)
{
return code;
}
+#endif /* #ifndef DISABLE_JIT */
+
/*
* mono_arch_get_argument_info:
* @csig: a method signature
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);
}
/*
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;
/*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)
return 2;
}
+#endif /* #ifndef DISABLE_JIT */
+
#ifndef __GNUC_PREREQ
#define __GNUC_PREREQ(maj, min) (0)
#endif
"svc 0x00000000\n"
:
: "r" (code), "r" (code + size), "r" (syscall)
- : "r0", "r1", "r7"
+ : "r0", "r1", "r7", "r2"
);
#else
__asm __volatile ("mov r0, %0\n"
#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;
int nargs;
guint32 stack_usage;
guint32 struct_ret;
+ gboolean vtype_retaddr;
ArgInfo ret;
ArgInfo sig_cookie;
ArgInfo args [1];
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 {
/* 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__
#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 (i8_align == 8 && ((*gr) & 1))
(*gr) ++;
#endif
+ ainfo->storage = RegTypeIRegPair;
ainfo->reg = *gr;
}
(*gr) ++;
}
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;
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);
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++;
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))
}
}
+ /* 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) {
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:
return cinfo;
}
+#ifndef DISABLE_JIT
/*
* Set var information according to the calling convention. arm version.
{
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;
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
/* 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;
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.
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++;
}
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)
{
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;
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);
}
}
+ /* 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;
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
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 (MonoMethodSignature *sig)
+dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
{
int i;
-#ifdef PLATFORM_WIN32
- return FALSE;
-#endif
-
- if (sig->hasthis + sig->param_count > PARAM_REGS)
+ if (sig->hasthis + sig->param_count > PARAM_REGS + DYN_CALL_STACK_ARGS)
return FALSE;
- switch (sig->ret->type) {
- case MONO_TYPE_VOID:
- case MONO_TYPE_I1:
- case MONO_TYPE_U1:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- case MONO_TYPE_I:
- case MONO_TYPE_U:
- /*
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- */
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_CHAR:
- case MONO_TYPE_STRING:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_PTR:
- break;
- case MONO_TYPE_GENERICINST:
- if (!MONO_TYPE_IS_REFERENCE (sig->ret))
- return FALSE;
+
+ switch (cinfo->ret.storage) {
+ case RegTypeNone:
+ case RegTypeGeneral:
+ case RegTypeIRegPair:
+ case RegTypeStructByAddr:
break;
- case MONO_TYPE_VAR:
- case MONO_TYPE_MVAR:
- /* assume gshared */
+ 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;
- handle_enum:
switch (t->type) {
case MONO_TYPE_R4:
case MONO_TYPE_R8:
- case MONO_TYPE_TYPEDBYREF:
- return FALSE;
- case MONO_TYPE_VALUETYPE:
- if (t->data.klass->enumtype) {
- t = mono_class_enum_basetype (t->data.klass);
- goto handle_enum;
- }
+#ifdef MONO_ARCH_SOFT_FLOAT
return FALSE;
- case MONO_TYPE_GENERICINST:
- if (!MONO_TYPE_IS_REFERENCE (t))
- return FALSE;
+#else
break;
- 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_BOOLEAN:
- case MONO_TYPE_CHAR:
- case MONO_TYPE_I1:
- case MONO_TYPE_U1:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- case MONO_TYPE_I:
- case MONO_TYPE_U:
+#endif
/*
case MONO_TYPE_I8:
case MONO_TYPE_U8:
+ return FALSE;
*/
- break;
- case MONO_TYPE_VAR:
- case MONO_TYPE_MVAR:
- /* assume gshared */
- break;
default:
- return FALSE;
break;
}
}
+
return TRUE;
}
-typedef struct {
- MonoMethodSignature *sig;
-} DynCallInfo;
-
-typedef struct {
- mgreg_t regs [PARAM_REGS];
- mgreg_t res;
-} DynCallArgs;
-
MonoDynCallInfo*
mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
{
- DynCallInfo *info;
+ ArchDynCallInfo *info;
+ CallInfo *cinfo;
- if (!dyn_call_supported (sig))
+ cinfo = get_call_info (NULL, sig, FALSE);
+
+ if (!dyn_call_supported (cinfo, sig)) {
+ g_free (cinfo);
return NULL;
+ }
- info = g_new0 (DynCallInfo, 1);
- // FIXME: Preprocess the info to speed up get_dyn_call_args ().
+ 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_get_dyn_call_args (MonoDynCallInfo *info, gpointer **args, guint8 *buf, int buf_len)
+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;
- MonoMethodSignature *sig = ((DynCallInfo*)info)->sig;
+ 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 (sig->hasthis) {
+
+ 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 [greg ++] = (mgreg_t)*(args [arg_index ++]);
+ p->regs [slot] = (mgreg_t)*arg;
continue;
}
case MONO_TYPE_PTR:
case MONO_TYPE_I:
case MONO_TYPE_U:
- /*
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- */
- p->regs [greg ++] = (mgreg_t)*(args [arg_index ++]);
- break;
- case MONO_TYPE_GENERICINST:
- g_assert (MONO_TYPE_IS_REFERENCE (t));
- p->regs [greg ++] = (mgreg_t)*(args [arg_index ++]);
+ p->regs [slot] = (mgreg_t)*arg;
break;
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_U1:
- p->regs [greg ++] = *(guint8*)(args [arg_index ++]);
+ p->regs [slot] = *(guint8*)arg;
break;
case MONO_TYPE_I1:
- p->regs [greg ++] = *(gint8*)(args [arg_index ++]);
+ p->regs [slot] = *(gint8*)arg;
break;
case MONO_TYPE_I2:
- p->regs [greg ++] = *(gint16*)(args [arg_index ++]);
+ p->regs [slot] = *(gint16*)arg;
break;
case MONO_TYPE_U2:
case MONO_TYPE_CHAR:
- p->regs [greg ++] = *(guint16*)(args [arg_index ++]);
+ p->regs [slot] = *(guint16*)arg;
break;
case MONO_TYPE_I4:
- p->regs [greg ++] = *(gint32*)(args [arg_index ++]);
+ p->regs [slot] = *(gint32*)arg;
break;
case MONO_TYPE_U4:
- p->regs [greg ++] = *(guint32*)(args [arg_index ++]);
+ 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 ();
}
}
-
- g_assert (greg <= PARAM_REGS);
}
void
-mono_arch_get_dyn_call_ret (MonoDynCallInfo *info, guint8 *buf, guint8 *ret)
+mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf)
{
- MonoMethodSignature *sig = ((DynCallInfo*)info)->sig;
+ 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 (sig->ret->type) {
+ switch (mono_type_get_underlying_type (sig->ret)->type) {
case MONO_TYPE_VOID:
*(gpointer*)ret = NULL;
break;
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_PTR:
- *(gpointer*)ret = (gpointer)((DynCallArgs*)buf)->res;
- break;
- case MONO_TYPE_GENERICINST:
- *(gpointer*)ret = (gpointer)((DynCallArgs*)buf)->res;
+ *(gpointer*)ret = (gpointer)res;
break;
case MONO_TYPE_I1:
- *(gint8*)ret = ((DynCallArgs*)buf)->res;
+ *(gint8*)ret = res;
break;
case MONO_TYPE_U1:
case MONO_TYPE_BOOLEAN:
- *(guint8*)ret = ((DynCallArgs*)buf)->res;
+ *(guint8*)ret = res;
break;
case MONO_TYPE_I2:
- *(gint16*)ret = ((DynCallArgs*)buf)->res;
+ *(gint16*)ret = res;
break;
case MONO_TYPE_U2:
case MONO_TYPE_CHAR:
- *(guint16*)ret = ((DynCallArgs*)buf)->res;
+ *(guint16*)ret = res;
break;
case MONO_TYPE_I4:
- *(gint32*)ret = ((DynCallArgs*)buf)->res;
+ *(gint32*)ret = res;
break;
case MONO_TYPE_U4:
- *(guint32*)ret = ((DynCallArgs*)buf)->res;
+ *(guint32*)ret = res;
break;
- /*
case MONO_TYPE_I8:
- *(gint64*)ret = ((DynCallArgs*)buf)->res;
- break;
case MONO_TYPE_U8:
- *(guint64*)ret = ((DynCallArgs*)buf)->res;
+ /* 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 ();
}
}
+#ifndef DISABLE_JIT
+
/*
* Allow tracing to work with this interface (with an optional argument)
*/
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;
return code;
}
+#endif /* #ifndef DISABLE_JIT */
+
typedef struct {
guchar *code;
const guchar *target;
}
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;
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;
}
}
- handle_thunk (TRUE, code, target);
+ handle_thunk (domain, TRUE, code, target);
return;
}
// 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
return code;
}
+gboolean
+mono_arm_thumb_supported (void)
+{
+ return thumb_supported;
+}
+
+#ifndef DISABLE_JIT
+
/*
* emit_load_volatile_arguments:
*
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;
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;
/*
} else
g_assert_not_reached ();
} else {
- if (ainfo->regtype == RegTypeGeneral) {
+ if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair) {
switch (ainfo->size) {
case 1:
case 2:
}
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;
if (ainfo->vtsize)
// FIXME:
NOT_IMPLEMENTED;
- } else if (ainfo->regtype == RegTypeStructByAddr) {
+ } else if (ainfo->storage == RegTypeStructByAddr) {
} else {
// FIXME:
NOT_IMPLEMENTED;
return code;
}
-#ifndef DISABLE_JIT
-
void
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);
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:
/* 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: {
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;
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 {\
default:
break;
}
- arm_patch (ip, target);
+ arm_patch_general (domain, ip, target);
}
}
+#ifndef DISABLE_JIT
+
/*
* Stack frame layout:
*
/* 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 {
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))
}
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 {
}
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;
//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);
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);
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;
}
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 */
}
+#endif /* #ifndef DISABLE_JIT */
+
static gboolean tls_offset_inited = FALSE;
void
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)
{
#ifdef MONO_ARCH_HAVE_IMT
+#ifndef DISABLE_JIT
+
void
mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
{
}
}
+#endif /* DISABLE_JIT */
+
MonoMethod*
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)
{
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 if (reg == ARMREG_SP)
+ if (reg == ARMREG_SP)
return (gpointer)ctx->esp;
- else {
- g_assert_not_reached ();
- return NULL;
+ 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;
+}