Merge pull request #1991 from esdrubal/seq_test_fix
[mono.git] / mono / mini / mini-arm.c
index b3a53134c971fb66a8f6007f53ddec696b0cee07..e92a280219623d591227daf9742c2b3c1ae58e0d 100644 (file)
@@ -749,6 +749,7 @@ static gpointer
 get_delegate_invoke_impl (MonoTrampInfo **info, gboolean has_target, gboolean param_count)
 {
        guint8 *code, *start;
+       GSList *unwind_ops = mono_arch_get_cie_program ();
 
        if (has_target) {
                start = code = mono_global_codeman_reserve (12);
@@ -780,10 +781,10 @@ get_delegate_invoke_impl (MonoTrampInfo **info, gboolean has_target, gboolean pa
        }
 
        if (has_target) {
-                *info = mono_tramp_info_create ("delegate_invoke_impl_has_target", start, code - start, NULL, NULL);
+                *info = mono_tramp_info_create ("delegate_invoke_impl_has_target", start, code - start, NULL, unwind_ops);
        } else {
                 char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", param_count);
-                *info = mono_tramp_info_create (name, start, code - start, NULL, NULL);
+                *info = mono_tramp_info_create (name, start, code - start, NULL, unwind_ops);
                 g_free (name);
        }
 
@@ -1515,8 +1516,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
                        cinfo->ret.reg = ARMREG_R0;
                        break;
                }
-               // FIXME: Only for variable types
-               if (mini_is_gsharedvt_type (t)) {
+               if (mini_is_gsharedvt_variable_type (t)) {
                        cinfo->ret.storage = RegTypeStructByAddr;
                        break;
                }
@@ -1634,7 +1634,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
                                add_general (&gr, &stack_size, ainfo, TRUE);
                                break;
                        }
-                       if (mini_is_gsharedvt_type (t)) {
+                       if (mini_is_gsharedvt_variable_type (t)) {
                                /* gsharedvt arguments are passed by ref */
                                g_assert (mini_is_gsharedvt_type (t));
                                add_general (&gr, &stack_size, ainfo, TRUE);
@@ -2809,8 +2809,8 @@ dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
 
                switch (ainfo->storage) {
                case RegTypeGeneral:
-                       break;
                case RegTypeIRegPair:
+               case RegTypeBaseGen:
                        break;
                case RegTypeBase:
                        if (ainfo->offset >= (DYN_CALL_STACK_ARGS * sizeof (gpointer)))
@@ -2925,12 +2925,16 @@ mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, g
                ArgInfo *ainfo = &dinfo->cinfo->args [i + sig->hasthis];
                int slot = -1;
 
-               if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair || ainfo->storage == RegTypeStructByVal)
+               if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair || ainfo->storage == RegTypeStructByVal) {
                        slot = ainfo->reg;
-               else if (ainfo->storage == RegTypeBase)
+               } else if (ainfo->storage == RegTypeBase) {
                        slot = PARAM_REGS + (ainfo->offset / 4);
-               else
+               } else if (ainfo->storage == RegTypeBaseGen) {
+                       /* slot + 1 is the first stack slot, so the code below will work */
+                       slot = 3;
+               } else {
                        g_assert_not_reached ();
+               }
 
                if (t->byref) {
                        p->regs [slot] = (mgreg_t)*arg;
@@ -3899,6 +3903,10 @@ handle_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar *
                                        /* Free entry */
                                        target_thunk = p;
                                        break;
+                               } else if (((guint32*)p) [2] == (guint32)target) {
+                                       /* Thunk already points to target */
+                                       target_thunk = p;
+                                       break;
                                }
                        }
                }
@@ -5975,6 +5983,22 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ins->backend.pc_offset = code - cfg->native_code;
                        bb->spill_slot_defs = g_slist_prepend_mempool (cfg->mempool, bb->spill_slot_defs, ins);
                        break;
+               case OP_GC_SAFE_POINT: {
+#if defined (USE_COOP_GC)
+                       const char *polling_func = NULL;
+                       guint8 *buf [1];
+
+                       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);
+                       code = emit_call_seq (cfg, code);
+                       arm_patch (buf [0], code);
+#endif
+                       break;
+               }
 
                default:
                        g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
@@ -6010,6 +6034,27 @@ mono_arch_register_lowlevel_calls (void)
                if (mono_arm_have_fast_tls ()) {
                        mono_register_jit_icall (mono_fast_get_tls_key, "mono_get_tls_key", mono_create_icall_signature ("ptr ptr"), TRUE);
                        mono_register_jit_icall (mono_fast_set_tls_key, "mono_set_tls_key", mono_create_icall_signature ("void ptr ptr"), TRUE);
+
+                       mono_tramp_info_register (
+                               mono_tramp_info_create (
+                                       "mono_get_tls_key",
+                                       (guint8*)mono_fast_get_tls_key,
+                                       (guint8*)mono_fast_get_tls_key_end - (guint8*)mono_fast_get_tls_key,
+                                       NULL,
+                                       mono_arch_get_cie_program ()
+                                       ),
+                               NULL
+                               );
+                       mono_tramp_info_register (
+                               mono_tramp_info_create (
+                                       "mono_set_tls_key",
+                                       (guint8*)mono_fast_set_tls_key,
+                                       (guint8*)mono_fast_set_tls_key_end - (guint8*)mono_fast_set_tls_key,
+                                       NULL,
+                                       mono_arch_get_cie_program ()
+                                       ),
+                               NULL
+                               );
                } else {
                        g_warning ("No fast tls on device. Using fallbacks.");
                        mono_register_jit_icall (mono_fallback_get_tls_key, "mono_get_tls_key", mono_create_icall_signature ("ptr ptr"), TRUE);
@@ -6147,10 +6192,10 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                 * FIXME: Optimize this.
                 */
                ARM_PUSH (code, (1 << ARMREG_R7) | (1 << ARMREG_LR));
-               ARM_MOV_REG_REG (code, ARMREG_R7, ARMREG_SP);
                prev_sp_offset += 8; /* r7 and lr */
                mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
                mono_emit_unwind_op_offset (cfg, code, ARMREG_R7, (- prev_sp_offset) + 0);
+               ARM_MOV_REG_REG (code, ARMREG_R7, ARMREG_SP);
        }
 
        if (!method->save_lmf) {
@@ -6614,6 +6659,9 @@ mono_arch_emit_epilog (MonoCompile *cfg)
         */
        code = cfg->native_code + cfg->code_len;
 
+       /* Save the uwind state which is needed by the out-of-line code */
+       mono_emit_unwind_op_remember_state (cfg, code);
+
        if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
                code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
        }
@@ -6649,7 +6697,7 @@ mono_arch_emit_epilog (MonoCompile *cfg)
        }
 
        if (method->save_lmf) {
-               int lmf_offset, reg, sp_adj, regmask;
+               int lmf_offset, reg, sp_adj, regmask, nused_int_regs = 0;
                /* all but r0-r3, sp and pc */
                pos += sizeof (MonoLMF) - (MONO_ARM_NUM_SAVED_REGS * sizeof (mgreg_t));
                lmf_offset = pos;
@@ -6671,15 +6719,33 @@ mono_arch_emit_epilog (MonoCompile *cfg)
                        regmask &= ~(1 << ARMREG_PC);
                /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
                code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage - lmf_offset + sp_adj);
+               for (i = 0; i < 16; i++) {
+                       if (regmask & (1 << i))
+                               nused_int_regs ++;
+               }
+               mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, ((iphone_abi ? 3 : 0) + nused_int_regs) * 4);
                /* restore iregs */
                ARM_POP (code, regmask); 
                if (iphone_abi) {
+                       for (i = 0; i < 16; i++) {
+                               if (regmask & (1 << i))
+                                       mono_emit_unwind_op_same_value (cfg, code, i);
+                       }
                        /* Restore saved r7, restore LR to PC */
                        /* Skip lr from the lmf */
+                       mono_emit_unwind_op_def_cfa_offset (cfg, code, 3 * 4);
                        ARM_ADD_REG_IMM (code, ARMREG_SP, ARMREG_SP, sizeof (gpointer), 0);
+                       mono_emit_unwind_op_def_cfa_offset (cfg, code, 2 * 4);
                        ARM_POP (code, (1 << ARMREG_R7) | (1 << ARMREG_PC));
                }
        } else {
+               int i, nused_int_regs = 0;
+
+               for (i = 0; i < 16; i++) {
+                       if (cfg->used_int_regs & (1 << i))
+                               nused_int_regs ++;
+               }
+
                if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
                        ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
                } else {
@@ -6687,17 +6753,32 @@ mono_arch_emit_epilog (MonoCompile *cfg)
                        ARM_ADD_REG_REG (code, ARMREG_SP, cfg->frame_reg, ARMREG_IP);
                }
 
+               if (cfg->frame_reg != ARMREG_SP) {
+                       mono_emit_unwind_op_def_cfa_reg (cfg, code, ARMREG_SP);
+               }
+
                if (iphone_abi) {
                        /* Restore saved gregs */
-                       if (cfg->used_int_regs)
+                       if (cfg->used_int_regs) {
+                               mono_emit_unwind_op_def_cfa_offset (cfg, code, (2 + nused_int_regs) * 4);
                                ARM_POP (code, cfg->used_int_regs);
+                               for (i = 0; i < 16; i++) {
+                                       if (cfg->used_int_regs & (1 << i))
+                                               mono_emit_unwind_op_same_value (cfg, code, i);
+                               }
+                       }
+                       mono_emit_unwind_op_def_cfa_offset (cfg, code, 2 * 4);
                        /* Restore saved r7, restore LR to PC */
                        ARM_POP (code, (1 << ARMREG_R7) | (1 << ARMREG_PC));
                } else {
+                       mono_emit_unwind_op_def_cfa_offset (cfg, code, (nused_int_regs + 1) * 4);
                        ARM_POP (code, cfg->used_int_regs | (1 << ARMREG_PC));
                }
        }
 
+       /* Restore the unwind state to be the same as before the epilog */
+       mono_emit_unwind_op_restore_state (cfg, code);
+
        cfg->code_len = code - cfg->native_code;
 
        g_assert (cfg->code_len < cfg->code_size);
@@ -6852,6 +6933,16 @@ mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code)
        return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
 }
 
+GSList*
+mono_arch_get_cie_program (void)
+{
+       GSList *l = NULL;
+
+       mono_add_unwind_op_def_cfa (l, (guint8*)NULL, (guint8*)NULL, ARMREG_SP, 0);
+
+       return l;
+}
+
 /* #define ENABLE_WRONG_METHOD_CHECK 1 */
 #define BASE_SIZE (6 * 4)
 #define BSEARCH_ENTRY_SIZE (4 * 4)
@@ -6920,6 +7011,7 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
 #ifdef ENABLE_WRONG_METHOD_CHECK
        char * cond;
 #endif
+       GSList *unwind_ops;
 
        size = BASE_SIZE;
 #ifdef USE_JUMP_TABLES
@@ -6974,6 +7066,8 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                code = mono_domain_code_reserve (domain, size);
        start = code;
 
+       unwind_ops = mono_arch_get_cie_program ();
+
 #ifdef DEBUG_IMT
        g_print ("Building IMT thunk for class %s %s entries %d code size %d code at %p end %p vtable %p fail_tramp %p\n", vtable->klass->name_space, vtable->klass->name, count, size, start, ((guint8*)start) + size, vtable, fail_tramp);
        for (i = 0; i < count; ++i) {
@@ -6984,6 +7078,7 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
 
 #ifdef USE_JUMP_TABLES
        ARM_PUSH3 (code, ARMREG_R0, ARMREG_R1, ARMREG_R2);
+       mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 3 * sizeof (mgreg_t));
 #define VTABLE_JTI 0
 #define IMT_METHOD_OFFSET 0
 #define TARGET_CODE_OFFSET 1
@@ -6998,10 +7093,13 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
        ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, VTABLE_JTI);
        set_jumptable_element (jte, VTABLE_JTI, vtable);
 #else
-       if (large_offsets)
+       if (large_offsets) {
                ARM_PUSH4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
-       else
+               mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 4 * sizeof (mgreg_t));
+       } else {
                ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
+               mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 2 * sizeof (mgreg_t));
+       }
        ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
        vtable_target = code;
        ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
@@ -7074,6 +7172,7 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                                code = load_element_with_regbase_cond (code, ARMREG_R1, ARMREG_R2, target_code_jti, ARMCOND_AL);
                                /* Restore registers */
                                ARM_POP3 (code, ARMREG_R0, ARMREG_R1, ARMREG_R2);
+                               mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 0);
                                /*  And branch */
                                ARM_BX (code, ARMREG_R1);
                                set_jumptable_element (jte, target_code_jti, item->value.target_code);
@@ -7105,6 +7204,7 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                                        ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_IP, ARMREG_R1);
                                        /* Restore registers and branch */
                                        ARM_POP3 (code, ARMREG_R0, ARMREG_R1, ARMREG_R2);
+                                       mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 0);
                                        ARM_BX (code, ARMREG_IP);
 #else
                                        vtable_offset_ins = code;
@@ -7121,11 +7221,15 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
 #ifdef USE_JUMP_TABLES
                                        ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, vtable_offset);
                                        ARM_POP3 (code, ARMREG_R0, ARMREG_R1, ARMREG_R2);
+                                       mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 0);
                                        ARM_BX (code, ARMREG_IP);
 #else
                                        ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
-                                       if (large_offsets)
+                                       if (large_offsets) {
+                                               mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 2 * sizeof (mgreg_t));
                                                ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
+                                       }
+                                       mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 0);
                                        ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
 #endif
                                }
@@ -7139,6 +7243,7 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                                code = load_element_with_regbase_cond (code, ARMREG_R1, ARMREG_R2, target_code_jti, ARMCOND_AL);
                                /* Restore registers */
                                ARM_POP3 (code, ARMREG_R0, ARMREG_R1, ARMREG_R2);
+                               mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, 0);
                                /* And branch */
                                ARM_BX (code, ARMREG_R1);
                                set_jumptable_element (jte, target_code_jti, fail_tramp);
@@ -7239,7 +7344,7 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
 
        g_assert (DISTANCE (start, code) <= size);
 
-       mono_tramp_info_register (mono_tramp_info_create (NULL, (guint8*)start, DISTANCE (start, code), NULL, NULL), domain);
+       mono_tramp_info_register (mono_tramp_info_create (NULL, (guint8*)start, DISTANCE (start, code), NULL, unwind_ops), domain);
 
        return start;
 }