diff mbox

[ARM,2/3] Epilogue using LDRD in ARM mode

Message ID 000d01cda6f4$7d7da630$7878f290$@yorsh@arm.com
State New
Headers show

Commit Message

Greta Yorsh Oct. 10, 2012, 2:35 p.m. UTC
Emit epilogue using LDRD in ARM mode when prefer_ldrd_strd is set.

ChangeLog

gcc/
 
2012-10-10  Sameera Deshpande  <sameera.deshpande at arm.com>
            Greta Yorsh  <Greta.Yorsh at arm.com>

        * config/arm/arm.c (arm_emit_ldrd_pop): New function.
        (arm_expand_epilogue): Used here.
        (arm_emit_multi_reg_pop): Add a special case for load of a single
register
        with writeback.
diff mbox

Patch

diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index f3e84f8..b106cd6 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -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);
             }