Merge pull request #3192 from esdrubal/sym-spec
[mono.git] / mono / mini / mini-arm.c
index 1dc4df6fd9e2ed6a33c66f05d18ccb0fbcac5727..4fbe7c06c0886c91d4418f029b016f019d6f2339 100644 (file)
@@ -20,6 +20,7 @@
 #include <mono/utils/mono-mmap.h>
 #include <mono/utils/mono-hwcap-arm.h>
 #include <mono/utils/mono-memory-model.h>
+#include <mono/utils/mono-threads-coop.h>
 
 #include "mini-arm.h"
 #include "mini-arm-tls.h"
@@ -1083,9 +1084,7 @@ mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
 void
 mono_arch_flush_icache (guint8 *code, gint size)
 {
-#if defined(MONO_CROSS_COMPILE) || defined(__native_client__)
-  // For Native Client we don't have to flush i-cache here,
-  // as it's being done by dyncode interface.
+#if defined(MONO_CROSS_COMPILE)
 #elif __APPLE__
        sys_icache_invalidate (code, size);
 #else
@@ -1364,10 +1363,25 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
                        cinfo->ret.nregs = nfields;
                        cinfo->ret.esize = esize;
                } else {
-                       if (is_pinvoke && mono_class_native_size (mono_class_from_mono_type (t), &align) <= sizeof (gpointer))
-                               cinfo->ret.storage = RegTypeStructByVal;
-                       else
+                       if (is_pinvoke) {
+                               int native_size = mono_class_native_size (mono_class_from_mono_type (t), &align);
+                               int max_size;
+
+#ifdef TARGET_WATCHOS
+                               max_size = 16;
+#else
+                               max_size = 4;
+#endif
+                               if (native_size <= max_size) {
+                                       cinfo->ret.storage = RegTypeStructByVal;
+                                       cinfo->ret.struct_size = native_size;
+                                       cinfo->ret.nregs = ALIGN_TO (native_size, 4) / 4;
+                               } else {
+                                       cinfo->ret.storage = RegTypeStructByAddr;
+                               }
+                       } else {
                                cinfo->ret.storage = RegTypeStructByAddr;
+                       }
                }
                break;
        case MONO_TYPE_VAR:
@@ -1520,6 +1534,27 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
                                        size = mini_type_stack_size_full (t, &align, FALSE);
                        }
                        DEBUG(g_print ("load %d bytes struct\n", size));
+
+#ifdef TARGET_WATCHOS
+                       /* Watchos pass large structures by ref */
+                       /* We only do this for pinvoke to make gsharedvt/dyncall simpler */
+                       if (sig->pinvoke && size > 16) {
+                               add_general (&gr, &stack_size, ainfo, TRUE);
+                               switch (ainfo->storage) {
+                               case RegTypeGeneral:
+                                       ainfo->storage = RegTypeStructByAddr;
+                                       break;
+                               case RegTypeBase:
+                                       ainfo->storage = RegTypeStructByAddrOnStack;
+                                       break;
+                               default:
+                                       g_assert_not_reached ();
+                                       break;
+                               }
+                               break;
+                       }
+#endif
+
                        align_size = size;
                        nwords = 0;
                        align_size += (sizeof (gpointer) - 1);
@@ -1806,21 +1841,16 @@ mono_arch_allocate_vars (MonoCompile *cfg)
 
        switch (cinfo->ret.storage) {
        case 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;
-               offset += sizeof(gpointer);
-               break;
        case RegTypeHFA:
                /* Allocate a local to hold the result, the epilog will copy it to the correct place */
                offset = ALIGN_TO (offset, 8);
                cfg->ret->opcode = OP_REGOFFSET;
                cfg->ret->inst_basereg = cfg->frame_reg;
                cfg->ret->inst_offset = offset;
-               // FIXME:
-               offset += 32;
+               if (cinfo->ret.storage == RegTypeStructByVal)
+                       offset += cinfo->ret.nregs * sizeof (gpointer);
+               else
+                       offset += 32;
                break;
        case RegTypeStructByAddr:
                ins = cfg->vret_addr;
@@ -2142,6 +2172,13 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
                linfo->ret.storage = LLVMArgVtypeRetAddr;
                linfo->vret_arg_index = cinfo->vret_arg_index;
                break;
+#if TARGET_WATCHOS
+       case RegTypeStructByVal:
+               /* LLVM models this by returning an int array */
+               linfo->ret.storage = LLVMArgAsIArgs;
+               linfo->ret.nslots = cinfo->ret.nregs;
+               break;
+#endif
        default:
                cfg->exception_message = g_strdup_printf ("unknown ret conv (%d)", cinfo->ret.storage);
                cfg->disable_llvm = TRUE;
@@ -2149,9 +2186,10 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
        }
 
        for (i = 0; i < n; ++i) {
+               LLVMArgInfo *lainfo = &linfo->args [i];
                ainfo = cinfo->args + i;
 
-               linfo->args [i].storage = LLVMArgNone;
+               lainfo->storage = LLVMArgNone;
 
                switch (ainfo->storage) {
                case RegTypeGeneral:
@@ -2159,11 +2197,15 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
                case RegTypeBase:
                case RegTypeBaseGen:
                case RegTypeFP:
-                       linfo->args [i].storage = LLVMArgNormal;
+                       lainfo->storage = LLVMArgNormal;
                        break;
                case RegTypeStructByVal:
-                       linfo->args [i].storage = LLVMArgAsIArgs;
-                       linfo->args [i].nslots = ainfo->struct_size / sizeof (gpointer);
+                       lainfo->storage = LLVMArgAsIArgs;
+                       lainfo->nslots = ainfo->struct_size / sizeof (gpointer);
+                       break;
+               case RegTypeStructByAddr:
+               case RegTypeStructByAddrOnStack:
+                       lainfo->storage = LLVMArgVtypeByRef;
                        break;
                default:
                        cfg->exception_message = g_strdup_printf ("ainfo->storage (%d)", ainfo->storage);
@@ -2191,10 +2233,14 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
 
        switch (cinfo->ret.storage) {
        case RegTypeStructByVal:
-               /* The JIT will transform this into a normal call */
-               call->vret_in_reg = TRUE;
-               break;
        case RegTypeHFA:
+               if (cinfo->ret.storage == RegTypeStructByVal && cinfo->ret.nregs == 1) {
+                       /* The JIT will transform this into a normal call */
+                       call->vret_in_reg = TRUE;
+                       break;
+               }
+               if (call->inst.opcode == OP_TAILCALL)
+                       break;
                /*
                 * The vtype is returned in registers, save the return area address in a local, and save the vtype into
                 * the location pointed to by it after call in emit_move_return_value ().
@@ -2308,19 +2354,12 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                                mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
                        }
                        break;
-               case RegTypeStructByAddr:
-                       NOT_IMPLEMENTED;
-#if 0
-                       /* FIXME: where si the data allocated? */
-                       arg->backend.reg3 = ainfo->reg;
-                       call->used_iregs |= 1 << ainfo->reg;
-                       g_assert_not_reached ();
-#endif
-                       break;
                case RegTypeStructByVal:
                case RegTypeGSharedVtInReg:
                case RegTypeGSharedVtOnStack:
                case RegTypeHFA:
+               case RegTypeStructByAddr:
+               case RegTypeStructByAddrOnStack:
                        MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
                        ins->opcode = OP_OUTARG_VT;
                        ins->sreg1 = in->dreg;
@@ -2466,10 +2505,12 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
 
        switch (ainfo->storage) {
        case RegTypeGSharedVtInReg:
+       case RegTypeStructByAddr:
                /* Pass by addr */
                mono_call_inst_add_outarg_reg (cfg, call, src->dreg, ainfo->reg, FALSE);
                break;
        case RegTypeGSharedVtOnStack:
+       case RegTypeStructByAddrOnStack:
                /* Pass by addr on stack */
                MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, src->dreg);
                break;
@@ -4039,10 +4080,16 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code)
        cinfo = call->call_info;
 
        switch (cinfo->ret.storage) {
+       case RegTypeStructByVal:
        case RegTypeHFA: {
                MonoInst *loc = cfg->arch.vret_addr_loc;
                int i;
 
+               if (cinfo->ret.storage == RegTypeStructByVal && cinfo->ret.nregs == 1) {
+                       /* The JIT treats this as a normal call */
+                       break;
+               }
+
                /* Load the destination address */
                g_assert (loc && loc->opcode == OP_REGOFFSET);
 
@@ -4052,11 +4099,34 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code)
                        code = mono_arm_emit_load_imm (code, ARMREG_LR, loc->inst_offset);
                        ARM_LDR_REG_REG (code, ARMREG_LR, loc->inst_basereg, ARMREG_LR);
                }
-               for (i = 0; i < cinfo->ret.nregs; ++i) {
-                       if (cinfo->ret.esize == 4)
-                               ARM_FSTS (code, cinfo->ret.reg + i, ARMREG_LR, i * 4);
-                       else
-                               ARM_FSTD (code, cinfo->ret.reg + (i * 2), ARMREG_LR, i * 8);
+
+               if (cinfo->ret.storage == RegTypeStructByVal) {
+                       int rsize = cinfo->ret.struct_size;
+
+                       for (i = 0; i < cinfo->ret.nregs; ++i) {
+                               g_assert (rsize >= 0);
+                               switch (rsize) {
+                               case 0:
+                                       break;
+                               case 1:
+                                       ARM_STRB_IMM (code, i, ARMREG_LR, i * 4);
+                                       break;
+                               case 2:
+                                       ARM_STRH_IMM (code, i, ARMREG_LR, i * 4);
+                                       break;
+                               default:
+                                       ARM_STR_IMM (code, i, ARMREG_LR, i * 4);
+                                       break;
+                               }
+                               rsize -= 4;
+                       }
+               } else {
+                       for (i = 0; i < cinfo->ret.nregs; ++i) {
+                               if (cinfo->ret.esize == 4)
+                                       ARM_FSTS (code, cinfo->ret.reg + i, ARMREG_LR, i * 4);
+                               else
+                                       ARM_FSTD (code, cinfo->ret.reg + (i * 2), ARMREG_LR, i * 8);
+                       }
                }
                return code;
        }
@@ -4293,7 +4363,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                break;
                        }
 
-                       ARM_DMB (code, ARM_DMB_SY);
+                       if (ins->backend.memory_barrier_kind != MONO_MEMORY_BARRIER_NONE)
+                               ARM_DMB (code, ARM_DMB_SY);
                        break;
                }
                case OP_ATOMIC_STORE_I1:
@@ -4304,7 +4375,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_ATOMIC_STORE_U4:
                case OP_ATOMIC_STORE_R4:
                case OP_ATOMIC_STORE_R8: {
-                       ARM_DMB (code, ARM_DMB_SY);
+                       if (ins->backend.memory_barrier_kind != MONO_MEMORY_BARRIER_NONE)
+                               ARM_DMB (code, ARM_DMB_SY);
 
                        code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
 
@@ -5834,17 +5906,15 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        bb->spill_slot_defs = g_slist_prepend_mempool (cfg->mempool, bb->spill_slot_defs, ins);
                        break;
                case OP_GC_SAFE_POINT: {
-                       const char *polling_func = NULL;
                        guint8 *buf [1];
 
                        g_assert (mono_threads_is_coop_enabled ());
 
-                       polling_func = "mono_threads_state_poll";
                        ARM_LDR_IMM (code, ARMREG_IP, ins->sreg1, 0);
                        ARM_CMP_REG_IMM (code, ARMREG_IP, 0, 0);
                        buf [0] = code;
                        ARM_B_COND (code, ARMCOND_EQ, 0);
-                       mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, polling_func);
+                       mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_threads_state_poll");
                        code = emit_call_seq (cfg, code);
                        arm_patch (buf [0], code);
                        break;
@@ -6232,6 +6302,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                        case RegTypeGeneral:
                        case RegTypeIRegPair:
                        case RegTypeGSharedVtInReg:
+                       case RegTypeStructByAddr:
                                switch (ainfo->size) {
                                case 1:
                                        if (arm_is_imm12 (inst->inst_offset))
@@ -6292,6 +6363,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                break;
                        case RegTypeBase:
                        case RegTypeGSharedVtOnStack:
+                       case RegTypeStructByAddrOnStack:
                                if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
                                        ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
                                } else {
@@ -6384,10 +6456,6 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                }
                                break;
                        }
-                       case 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);
                        default:
                                g_assert_not_reached ();
                                break;
@@ -6511,11 +6579,23 @@ mono_arch_emit_epilog (MonoCompile *cfg)
        case RegTypeStructByVal: {
                MonoInst *ins = cfg->ret;
 
-               if (arm_is_imm12 (ins->inst_offset)) {
-                       ARM_LDR_IMM (code, ARMREG_R0, ins->inst_basereg, ins->inst_offset);
+               if (cinfo->ret.nregs == 1) {
+                       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);
+                       }
                } 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);
+                       for (i = 0; i < cinfo->ret.nregs; ++i) {
+                               int offset = ins->inst_offset + (i * 4);
+                               if (arm_is_imm12 (offset)) {
+                                       ARM_LDR_IMM (code, i, ins->inst_basereg, offset);
+                               } else {
+                                       code = mono_arm_emit_load_imm (code, ARMREG_LR, offset);
+                                       ARM_LDR_REG_REG (code, i, ins->inst_basereg, ARMREG_LR);
+                               }
+                       }
                }
                break;
        }