stack_pointer_rtx, stack_pointer_rtx);
}
+/* This function hepls to handle situation when we need to make
+ multiple pop, but does not have lr or pc registers inside
+ saved_regs_mask and can not add cfa_notes.
+ This function is based on arm_emit_multi_reg_pop. */
+static void
+arm_emit_multi_reg_pop_no_return (unsigned long saved_regs_mask)
+{
+ int num_regs = 0;
+ int i, j;
+ rtx par;
+ rtx dwarf = NULL_RTX;
+ rtx tmp, reg;
+ int emit_update = 1;
+
+ for (i = 0; i <= LAST_ARM_REGNUM; i++)
+ if (saved_regs_mask & (1 << i))
+ num_regs++;
+
+ gcc_assert (num_regs && num_regs <= 16);
+
+ /* If SP is in reglist, then we don't emit SP update insn. */
+ emit_update = (saved_regs_mask & (1 << SP_REGNUM)) ? 0 : 1;
+
+ /* The parallel needs to hold num_regs SETs
+ and one SET for the stack update. */
+ par = gen_rtx_PARALLEL (VOIDmode,
+ rtvec_alloc (num_regs + emit_update));
+ if (emit_update)
+ {
+ /* Increment the stack pointer, based on there being
+ num_regs 4-byte registers to restore. */
+ tmp
+ = gen_rtx_SET (stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx, 4 * num_regs));
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ XVECEXP (par, 0, 0) = tmp;
+ }
+
+ for (j = 0, i = 0; j < num_regs; i++)
+ if (saved_regs_mask & (1 << i))
+ {
+ reg = gen_rtx_REG (SImode, i);
+ if ((num_regs == 1) && emit_update)
+ {
+ /* Emit single load with writeback. */
+ tmp = gen_frame_mem (SImode,
+ gen_rtx_POST_INC (Pmode, stack_pointer_rtx));
+ tmp = emit_insn (gen_rtx_SET (reg, tmp));
+ REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+ return;
+ }
+
+ tmp = gen_rtx_SET (
+ reg, gen_frame_mem (SImode,
+ plus_constant (Pmode, stack_pointer_rtx, 4 * j)));
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ XVECEXP (par, 0, j + emit_update) = tmp;
+ dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+ j++;
+ }
+
+ par = emit_insn (par);
+}
+
/* Generate and emit an insn pattern that we will recognize as a pop_multi
of NUM_REGS consecutive VFP regs, starting at FIRST_REG.
switch (to)
{
case THUMB_HARD_FRAME_POINTER_REGNUM:
+ if (TARGET_THUMB_STACK_UNWIND)
+ /* In this case the hard frame pointer points to the lr in the stack
+ frame. So offset is frame - saved_args. */
+ return offsets->frame - offsets->saved_args;
return 0;
case FRAME_POINTER_REGNUM:
switch (to)
{
case THUMB_HARD_FRAME_POINTER_REGNUM:
+ if (TARGET_THUMB_STACK_UNWIND)
+ /* The hard frame_pointer points to the top entry in the
+ stack frame. The soft frame pointer points to the bottom
+ entry in the stack. */
+ return offsets->frame - offsets->soft_frame;
return 0;
case ARM_HARD_FRAME_POINTER_REGNUM:
if ((func_type == ARM_FT_ISR || func_type == ARM_FT_FIQ)
&& (live_regs_mask & (1 << LR_REGNUM)) != 0
&& !(frame_pointer_needed && TARGET_APCS_FRAME)
- && TARGET_ARM)
+ && (TARGET_ARM || TARGET_THUMB_STACK_UNWIND))
{
rtx lr = gen_rtx_REG (SImode, LR_REGNUM);
&& !IS_INTERRUPT (func_type))
arm_emit_strd_push (live_regs_mask);
else
- {
+ {
insn = emit_multi_reg_push (live_regs_mask, live_regs_mask);
- RTX_FRAME_RELATED_P (insn) = 1;
- }
- }
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ }
else
- {
- insn = emit_multi_reg_push (live_regs_mask, dwarf_regs_mask);
- RTX_FRAME_RELATED_P (insn) = 1;
- }
+ {
+ /* In case we want to have frame pointer register to follow
+ lr register on the stack, we need to modify stack layout.
+ For example we have to save on the stack following registers
+ {r4, ... r11, lr}, but with thumb mode frame pointer
+ register is r7, so this layout is not correct. We will modify it
+ from
+ push {r4, ... r11, lr}
+ to
+ push {r4, ... r7, lr}
+ push {r8, ..., r11}
+ Registers should be pushed in ascending order. */
+ if (TARGET_THUMB_STACK_UNWIND && (live_regs_mask & (1 << 8))
+ /* Check for non-leaf fucntion. */
+ && (live_regs_mask & (1 << LR_REGNUM)))
+ {
+ unsigned long first_regs_mask, dwarf_first_regs_mask,
+ second_regs_mask, dwarf_second_regs_mask;
+
+ /* Save info about first 8 registers from r0 to r7 and
+ add lr for this mask. */
+ first_regs_mask = dwarf_first_regs_mask
+ = (live_regs_mask & 0xff) | (1 << LR_REGNUM);
+ /* Add all left registers from r8 and delete lr. */
+ second_regs_mask = dwarf_second_regs_mask
+ = (live_regs_mask & ~0xff) & ~(1 << LR_REGNUM);
+ insn
+ = emit_multi_reg_push (first_regs_mask, dwarf_first_regs_mask);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ insn = emit_multi_reg_push (second_regs_mask,
+ dwarf_second_regs_mask);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ else
+ {
+ insn = emit_multi_reg_push (live_regs_mask, dwarf_regs_mask);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ }
}
if (! IS_VOLATILE (func_type))
saved_regs += arm_save_coproc_regs ();
- if (frame_pointer_needed && TARGET_ARM)
+ /* Set frame pointer register (r7) to lr on the stack
+ for TARGET_THUMB_STACK_UNWIND. */
+ if (frame_pointer_needed && (TARGET_ARM || TARGET_THUMB_STACK_UNWIND))
{
/* Create the new frame pointer. */
if (TARGET_APCS_FRAME)
}
- if (frame_pointer_needed && TARGET_THUMB2)
+ if (frame_pointer_needed && (TARGET_THUMB2 && !TARGET_THUMB_FP))
thumb_set_frame_pointer (offsets);
if (flag_pic && arm_pic_register != INVALID_REGNUM)
emit_jump_insn (par);
}
else
- {
- saved_regs_mask &= ~ (1 << LR_REGNUM);
- saved_regs_mask |= (1 << PC_REGNUM);
- arm_emit_multi_reg_pop (saved_regs_mask);
- }
+ {
+ /* We have made multiple push for thumb, when frame pointer
+ is needed. So, make multiple pop in the same order. */
+ if (TARGET_THUMB_STACK_UNWIND && (saved_regs_mask & (1 << 8)))
+ {
+ unsigned long first_regs_mask = saved_regs_mask & 0xff;
+ unsigned long second_regs_mask = saved_regs_mask & ~0xff;
+
+ first_regs_mask |= (1 << PC_REGNUM);
+ second_regs_mask &= ~((1 << PC_REGNUM) | (1 << LR_REGNUM));
+ arm_emit_multi_reg_pop_no_return (second_regs_mask);
+ arm_emit_multi_reg_pop (first_regs_mask);
+ }
+ else
+ {
+ saved_regs_mask &= ~(1 << LR_REGNUM);
+ saved_regs_mask |= (1 << PC_REGNUM);
+ arm_emit_multi_reg_pop (saved_regs_mask);
+ }
+ }
}
else
{
}
else
{
- /* In Thumb-2 mode, the frame pointer points to the last saved
- register. */
- amount = offsets->locals_base - offsets->saved_regs;
+ /* For TARGET_THUMB_STACK_UNWIND the frame pointer points to the
+ bottom of the stack. */
+ if (TARGET_THUMB_STACK_UNWIND)
+ amount = offsets->frame - offsets->saved_regs;
+ else
+ /* In Thumb-2 mode, the frame pointer points to the last saved
+ register. */
+ amount = offsets->locals_base - offsets->saved_regs;
+
if (amount)
{
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
}
else
{
- if (TARGET_LDRD
+ if (TARGET_LDRD
&& current_tune->prefer_ldrd_strd
- && !optimize_function_for_size_p (cfun))
- {
+ && !optimize_function_for_size_p (cfun))
+ {
if (TARGET_THUMB2)
thumb2_emit_ldrd_pop (saved_regs_mask);
else if (TARGET_ARM && !IS_INTERRUPT (func_type))
else
arm_emit_multi_reg_pop (saved_regs_mask);
}
- else
- arm_emit_multi_reg_pop (saved_regs_mask);
- }
+ else
+ {
+ /* Make multiple pop in the same order as we done multiple push.
+ */
+ if (TARGET_THUMB_STACK_UNWIND && (saved_regs_mask & (1 << 8))
+ && ((saved_regs_mask & (1 << LR_REGNUM))
+ || (saved_regs_mask & (1 << PC_REGNUM))))
+ {
+ unsigned long first_regs_mask = saved_regs_mask & 0xff;
+ unsigned long second_regs_mask = saved_regs_mask & ~0xff;
+
+ if (saved_regs_mask & (1 << PC_REGNUM))
+ {
+ first_regs_mask |= (1 << PC_REGNUM);
+ second_regs_mask &= ~(1 << PC_REGNUM);
+ }
+ else
+ {
+ first_regs_mask |= (1 << LR_REGNUM);
+ second_regs_mask &= ~(1 << LR_REGNUM);
+ }
+ arm_emit_multi_reg_pop_no_return (second_regs_mask);
+ arm_emit_multi_reg_pop (first_regs_mask);
+ }
+ else
+ arm_emit_multi_reg_pop (saved_regs_mask);
+ }
+ }
if (return_in_pc)
return;