return frame_size;
}
-static gpointer
-decode_vcall_slot_from_ldr (guint32 ldr, mgreg_t *regs, int *displacement)
-{
- char *o = NULL;
- int reg, offset = 0;
- reg = (ldr >> 16 ) & 0xf;
- offset = ldr & 0xfff;
- 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 = (gpointer)regs [reg];
-
- *displacement = offset;
- return o;
-}
-
-gpointer
-mono_arch_get_vcall_slot (guint8 *code_ptr, mgreg_t *regs, int *displacement)
-{
- guint32* code = (guint32*)code_ptr;
-
- /* Locate the address of the method-specific trampoline. The call using
- the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
- looks something like this:
-
- ldr rA, rX, #offset
- mov lr, pc
- mov pc, rA
- or better:
- mov lr, pc
- ldr pc, rX, #offset
-
- The call sequence could be also:
- ldr ip, pc, 0
- b skip
- function pointer literal
- skip:
- mov lr, pc
- mov pc, ip
- Note that on ARM5+ we can use one instruction instead of the last two.
- Therefore, we need to locate the 'ldr rA' instruction to know which
- register was used to hold the method addrs.
- */
-
- /* This is the instruction after "ldc pc, xxx", "mov pc, xxx" or "bl xxx" could be either the IMT value or some other instruction*/
- --code;
-
- /* Three possible code sequences can happen here:
- * interface call:
- *
- * add lr, [pc + #4]
- * ldr pc, [rX - #offset]
- * .word IMT value
- *
- * virtual call:
- *
- * mov lr, pc
- * ldr pc, [rX - #offset]
- *
- * direct branch with bl:
- *
- * bl #offset
- *
- * direct branch with mov:
- *
- * mv pc, rX
- *
- * We only need to identify interface and virtual calls, the others can be ignored.
- *
- */
- if (IS_LDR_PC (code [-1]) && code [-2] == ADD_LR_PC_4)
- return decode_vcall_slot_from_ldr (code [-1], regs, displacement);
-
- if (IS_LDR_PC (code [0]) && code [-1] == MOV_LR_PC)
- return decode_vcall_slot_from_ldr (code [0], regs, displacement);
-
- return NULL;
-}
-
#define MAX_ARCH_DELEGATE_PARAMS 3
static gpointer
}
gpointer
-mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, mgreg_t *regs, guint8 *code)
+mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
{
- /* FIXME: handle returning a struct */
- if (MONO_TYPE_ISSTRUCT (sig->ret))
- return (gpointer)regs [ARMREG_R1];
return (gpointer)regs [ARMREG_R0];
}
regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
- if (!(cfg->compile_aot || cfg->uses_rgctx_reg))
+ if (!(cfg->compile_aot || cfg->uses_rgctx_reg || COMPILE_LLVM (cfg)))
/* V5 is reserved for passing the vtable/rgctx/IMT method */
regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
/*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
typedef struct {
int nargs;
guint32 stack_usage;
- guint32 struct_ret;
gboolean vtype_retaddr;
+ /* The index of the vret arg in the argument list */
+ int vret_arg_index;
ArgInfo ret;
ArgInfo sig_cookie;
ArgInfo args [1];
}
static CallInfo*
-get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke)
+get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke)
{
- guint i, gr;
+ guint i, gr, pstart;
int n = sig->hasthis + sig->param_count;
MonoType *simpletype;
guint32 stack_size = 0;
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;
}
}
+ pstart = 0;
n = 0;
- if (sig->hasthis) {
- add_general (&gr, &stack_size, cinfo->args + n, TRUE);
- n++;
+ /*
+ * To simplify get_this_arg_reg () and LLVM integration, emit the vret arg after
+ * the first argument, allowing 'this' to be always passed in the first arg reg.
+ * Also do this if the first argument is a reference type, since virtual calls
+ * are sometimes made using calli without sig->hasthis set, like in the delegate
+ * invoke wrappers.
+ */
+ if (cinfo->vtype_retaddr && !is_pinvoke && (sig->hasthis || (sig->param_count > 0 && MONO_TYPE_IS_REFERENCE (mini_type_get_underlying_type (gsctx, sig->params [0]))))) {
+ if (sig->hasthis) {
+ add_general (&gr, &stack_size, cinfo->args + 0, TRUE);
+ } else {
+ add_general (&gr, &stack_size, &cinfo->args [sig->hasthis + 0], TRUE);
+ pstart = 1;
+ }
+ n ++;
+ add_general (&gr, &stack_size, &cinfo->ret, TRUE);
+ cinfo->vret_arg_index = 1;
+ } else {
+ /* this */
+ if (sig->hasthis) {
+ add_general (&gr, &stack_size, cinfo->args + 0, TRUE);
+ n ++;
+ }
+
+ if (cinfo->vtype_retaddr)
+ add_general (&gr, &stack_size, &cinfo->ret, TRUE);
}
- DEBUG(printf("params: %d\n", sig->param_count));
- for (i = 0; i < sig->param_count; ++i) {
+
+ DEBUG(printf("params: %d\n", sig->param_count));
+ for (i = pstart; i < sig->param_count; ++i) {
if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
/* Prevent implicit arguments and sig_cookie from
being passed in registers */
sig = mono_method_signature (cfg->method);
if (!cfg->arch.cinfo)
- cfg->arch.cinfo = get_call_info (cfg->mempool, sig, sig->pinvoke);
+ cfg->arch.cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig, sig->pinvoke);
cinfo = cfg->arch.cinfo;
/* FIXME: this will change when we use FP as gcc does */
cfg->used_int_regs |= 1 << frame_reg;
}
- if (cfg->compile_aot || cfg->uses_rgctx_reg)
+ if (cfg->compile_aot || cfg->uses_rgctx_reg || COMPILE_LLVM (cfg))
/* V5 is reserved for passing the vtable/rgctx/IMT method */
cfg->used_int_regs |= (1 << ARMREG_V5);
sig = mono_method_signature (cfg->method);
if (!cfg->arch.cinfo)
- cfg->arch.cinfo = get_call_info (cfg->mempool, sig, sig->pinvoke);
+ cfg->arch.cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig, sig->pinvoke);
cinfo = cfg->arch.cinfo;
if (cinfo->ret.storage == RegTypeStructByVal)
n = sig->param_count + sig->hasthis;
- cinfo = get_call_info (cfg->mempool, sig, sig->pinvoke);
+ cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig, sig->pinvoke);
linfo = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMCallInfo) + (sizeof (LLVMArgInfo) * n));
sig = call->signature;
n = sig->param_count + sig->hasthis;
- cinfo = get_call_info (NULL, sig, sig->pinvoke);
+ cinfo = get_call_info (cfg->generic_sharing_context, NULL, sig, sig->pinvoke);
for (i = 0; i < n; ++i) {
ArgInfo *ainfo = cinfo->args + i;
ArchDynCallInfo *info;
CallInfo *cinfo;
- cinfo = get_call_info (NULL, sig, FALSE);
+ cinfo = get_call_info (NULL, NULL, sig, FALSE);
if (!dyn_call_supported (cinfo, sig)) {
g_free (cinfo);
pos = 0;
- cinfo = get_call_info (NULL, sig, sig->pinvoke);
+ cinfo = get_call_info (cfg->generic_sharing_context, NULL, sig, sig->pinvoke);
if (MONO_TYPE_ISSTRUCT (sig->ret)) {
ArgInfo *ainfo = &cinfo->ret;
*/
mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
max_len += 4 * GPOINTER_TO_INT (ins->klass);
- if (offset > (cfg->code_size - max_len - 16)) {
+ if (offset + max_len > (cfg->code_size - 16)) {
cfg->code_size += max_len;
cfg->code_size *= 2;
cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
/* load arguments allocated to register from the stack */
pos = 0;
- cinfo = get_call_info (NULL, sig, sig->pinvoke);
+ cinfo = get_call_info (cfg->generic_sharing_context, NULL, sig, sig->pinvoke);
if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage != RegTypeStructByVal) {
ArgInfo *ainfo = &cinfo->ret;
/* *(lmf_addr) = r1 */
ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
/* Skip method (only needed for trampoline LMF frames) */
- ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
+ ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, esp));
/* save the current IP */
ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
return MONO_EXC_NULL_REF;
if (strcmp (name, "ArrayTypeMismatchException") == 0)
return MONO_EXC_ARRAY_TYPE_MISMATCH;
+ if (strcmp (name, "ArgumentException") == 0)
+ return MONO_EXC_ARGUMENT;
g_error ("Unknown intrinsic exception %s\n", name);
return -1;
}
mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
}
- } else if (cfg->generic_context || imt_arg) {
+ } else if (cfg->generic_context || imt_arg || mono_use_llvm) {
/* Always pass in a register for simplicity */
call->dynamic_imt_arg = TRUE;
{
guint32 *code_ptr = (guint32*)code;
code_ptr -= 2;
+
+ if (mono_use_llvm)
+ /* Passed in V5 */
+ return (MonoMethod*)regs [ARMREG_V5];
+
/* The IMT value is stored in the code stream right after the LDC instruction. */
if (!IS_LDR_PC (code_ptr [0])) {
g_warning ("invalid code stream, instruction before IMT value is not a LDC in %s() (code %p value 0: 0x%x -1: 0x%x -2: 0x%x)", __FUNCTION__, code, code_ptr [2], code_ptr [1], code_ptr [0]);
item->chunk_size += WMC_SIZE;
#endif
}
- if (fail_case)
+ if (fail_case) {
item->chunk_size += 16;
+ large_offsets = TRUE;
+ }
item->chunk_size += CALL_SIZE;
} else {
item->chunk_size += BSEARCH_ENTRY_SIZE;
vtable_target = code;
ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
- /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
- ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
- ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
+ if (mono_use_llvm) {
+ /* LLVM always passes the IMT method in R5 */
+ ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_V5);
+ } else {
+ /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
+ ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
+ ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
+ }
for (i = 0; i < count; ++i) {
MonoIMTCheckItem *item = imt_entries [i];