Remove the MonoGenericContext/MonoMethod arguments from the get_this_arg () and get_u...
[mono.git] / mono / mini / mini-arm.c
index 1355c5a2ba4cd5393123309f9ab7666106fefee6..0454997f72cf09aa6a2f2e6ad4601630b49ece00 100644 (file)
@@ -306,85 +306,6 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit
        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
@@ -509,11 +430,8 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
 }
 
 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];
 }
 
@@ -680,7 +598,7 @@ mono_arch_get_global_int_regs (MonoCompile *cfg)
        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));*/
@@ -761,8 +679,9 @@ typedef struct {
 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];
@@ -835,9 +754,9 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
 }
 
 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;
@@ -858,19 +777,42 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke)
                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 */
@@ -1081,7 +1023,7 @@ mono_arch_allocate_vars (MonoCompile *cfg)
        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 */
@@ -1109,7 +1051,7 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                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);
 
@@ -1290,7 +1232,7 @@ mono_arch_create_vars (MonoCompile *cfg)
        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)
@@ -1360,7 +1302,7 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
 
        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));
 
@@ -1409,7 +1351,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
        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;
@@ -1783,7 +1725,7 @@ mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
        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);
@@ -2990,7 +2932,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
 
        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;
@@ -3850,7 +3792,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                         */
                        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);
@@ -4595,7 +4537,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        /* 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;
@@ -4817,7 +4759,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                /* *(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));
@@ -4973,6 +4915,8 @@ exception_id_by_name (const char *name)
                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;
 }
@@ -5132,7 +5076,7 @@ mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt
 
                        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;
@@ -5162,6 +5106,11 @@ mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
 {
        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]);
@@ -5231,8 +5180,10 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                                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;
@@ -5266,9 +5217,14 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
        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];