@@ -16208,6 +16208,17 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
if (saved_regs_mask & (1 << i))
{
reg = gen_rtx_REG (SImode, i);
+ if ((num_regs == 1) && emit_update && !return_in_pc)
+ {
+ /* Emit single load with writeback. */
+ tmp = gen_frame_mem (SImode,
+ gen_rtx_POST_INC (Pmode,
+ stack_pointer_rtx));
+ tmp = emit_insn (gen_rtx_SET (VOIDmode, reg, tmp));
+ REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+ return;
+ }
+
tmp = gen_rtx_SET (VOIDmode,
reg,
gen_frame_mem
@@ -16439,6 +16450,109 @@ thumb2_emit_ldrd_pop (unsigned long saved_regs_mask)
return;
}
+/* LDRD in ARM mode needs consecutive registers to be stored. This function
+ keeps accumulating non-consecutive registers until first consecutive register
+ pair is found. It then generates multi-reg POP for all accumulated
+ registers, and then generates LDRD with write-back for consecutive register
+ pair. This process is repeated until all the registers are loaded from
+ stack. multi register POP takes care of lone registers as well. However,
+ LDRD cannot be generated for PC, as results are unpredictable. Hence, if PC
+ is in SAVED_REGS_MASK, generate multi-reg POP with RETURN or LDR with RETURN
+ depending upon number of registers in REGS_TO_BE_POPPED_MASK. */
+static void
+arm_emit_ldrd_pop (unsigned long saved_regs_mask)
+{
+ int num_regs = 0;
+ int i, j;
+ rtx par = NULL_RTX;
+ rtx insn = NULL_RTX;
+ rtx dwarf = NULL_RTX;
+ rtx tmp;
+ unsigned long regs_to_be_popped_mask = 0;
+
+ for (i = 0; i <= LAST_ARM_REGNUM; i++)
+ if (saved_regs_mask & (1 << i))
+ num_regs++;
+
+ gcc_assert (num_regs && num_regs <= 16);
+
+ for (i = 0, j = 0; i < num_regs; j++)
+ if (saved_regs_mask & (1 << j))
+ {
+ i++;
+ if ((j % 2) == 0
+ && (saved_regs_mask & (1 << (j + 1)))
+ && (j + 1) != SP_REGNUM
+ && (j + 1) != PC_REGNUM
+ && regs_to_be_popped_mask)
+ {
+ /* Current register and next register form register pair for which
+ LDRD can be generated. Generate POP for accumulated registers
+ and reset regs_to_be_popped_mask. SP should be handled here as
+ the results are unpredictable if register being stored is same
+ as index register (in this case, SP). PC is always the last
+ register being popped. Hence, we don't have to worry about PC
+ here. */
+ arm_emit_multi_reg_pop (regs_to_be_popped_mask);
+ regs_to_be_popped_mask = 0;
+ continue;
+ }
+
+ regs_to_be_popped_mask |= (1 << j);
+
+ if ((j % 2) == 1
+ && (saved_regs_mask & (1 << (j - 1)))
+ && j != SP_REGNUM
+ && j != PC_REGNUM)
+ {
+ /* Generate an LDRD for register pair R_<j>, R_<j+1>. The pattern
+ generated here is equivalent to
+ [(SET SP, (PLUS SP, 8))
+ (SET R_<j-1>, (MEM SP))
+ (SET R_<j>, (MEM (PLUS SP, 4)))]. */
+ tmp = gen_rtx_SET (DImode,
+ gen_rtx_REG (DImode, j - 1),
+ gen_frame_mem (DImode,
+ gen_rtx_POST_INC (Pmode,
+ stack_pointer_rtx)));
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ dwarf = alloc_reg_note (REG_CFA_RESTORE,
+ gen_rtx_REG (SImode, j - 1),
+ dwarf);
+ dwarf = alloc_reg_note (REG_CFA_RESTORE,
+ gen_rtx_REG (SImode, j),
+ dwarf);
+ insn = emit_insn (tmp);
+ REG_NOTES (insn) = dwarf;
+ regs_to_be_popped_mask = 0;
+ dwarf = NULL_RTX;
+ }
+ }
+
+ if (regs_to_be_popped_mask)
+ {
+ /* single PC pop can happen here. Take care of that. */
+ if (regs_to_be_popped_mask == (1 << PC_REGNUM))
+ {
+ /* Only PC is to be popped. */
+ par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
+ XVECEXP (par, 0, 0) = ret_rtx;
+ tmp = gen_rtx_SET (SImode,
+ gen_rtx_REG (SImode, PC_REGNUM),
+ gen_frame_mem (SImode,
+ gen_rtx_POST_INC (SImode,
+ stack_pointer_rtx)));
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ XVECEXP (par, 0, 1) = tmp;
+ emit_jump_insn (par);
+ }
+ else
+ arm_emit_multi_reg_pop (regs_to_be_popped_mask);
+ }
+
+ return;
+}
+
/* Calculate the size of the return value that is passed in registers. */
static unsigned
arm_size_return_regs (void)
@@ -23402,6 +23516,8 @@ arm_expand_epilogue (bool really_return)
{
if (TARGET_THUMB2)
thumb2_emit_ldrd_pop (saved_regs_mask);
+ else if (TARGET_ARM)
+ arm_emit_ldrd_pop (saved_regs_mask);
else
arm_emit_multi_reg_pop (saved_regs_mask);
}