[arm] Fix unwind info for managed methods
authorVlad Brezae <brezaevlad@gmail.com>
Fri, 19 Jun 2015 00:55:46 +0000 (17:55 -0700)
committerVlad Brezae <brezaevlad@gmail.com>
Wed, 29 Jul 2015 22:36:56 +0000 (15:36 -0700)
Add unwind information for the epilog.

mono/mini/mini-arm.c

index 05da1c8fcbae0f8a0f038a601da7686ec6137db1..eca94c03084d468fda84fa6e432a3679babb6c6b 100644 (file)
@@ -6148,10 +6148,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) {
@@ -6615,6 +6615,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);
        }
@@ -6650,7 +6653,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;
@@ -6672,15 +6675,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 {
@@ -6688,17 +6709,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);