diff mbox

[ARM] PR71061, length pop* pattern in epilogue correctly

Message ID 5735DCAD.8050808@foss.arm.com
State New
Headers show

Commit Message

Jiong Wang May 13, 2016, 1:54 p.m. UTC
For thumb mode, this is causing wrong size calculation and may affect
some rtl pass, for example bb-order where copy_bb_p needs accurate insn
length info.

This have eventually part of the reason for
https://gcc.gnu.org/ml/gcc-patches/2016-05/msg00639.html  where bb-order
failed to do the bb copy.

For the fix, I think we should extend arm_attr_length_push_multi to pop*
pattern.

OK for trunk?

2016-05-13  Jiong. Wang <jiong.wang@arm.com>

gcc/
   PR target/71061
   * config/arm/arm-protos.h (arm_attr_length_push_multi): Rename to
   "arm_attr_length_pp_multi".  Add one parameter "first_index".
   * config/arm/arm.md (*push_multi): Use new function.
   (*load_multiple_with_writeback): Set "length" attribute.
   (*pop_multiple_with_writeback_and_return): Likewise.
   (*pop_multiple_with_return): Likewise.
diff mbox

Patch

diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index d8179c4..d9a09c0 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -162,7 +162,7 @@  extern const char *arm_output_shift(rtx *, int);
 extern const char *arm_output_iwmmxt_shift_immediate (const char *, rtx *, bool);
 extern const char *arm_output_iwmmxt_tinsr (rtx *);
 extern unsigned int arm_sync_loop_insns (rtx , rtx *);
-extern int arm_attr_length_push_multi(rtx, rtx);
+extern int arm_attr_length_pp_multi(rtx, rtx, int);
 extern void arm_expand_compare_and_swap (rtx op[]);
 extern void arm_split_compare_and_swap (rtx op[]);
 extern void arm_split_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx, rtx);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 71b5143..0ba98e1 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -27729,14 +27729,15 @@  arm_preferred_rename_class (reg_class_t rclass)
     return NO_REGS;
 }
 
-/* Compute the atrribute "length" of insn "*push_multi".
-   So this function MUST be kept in sync with that insn pattern.  */
+/* Compute the attribute "length" of an insn which performs a push/pop on
+   multiple registers.  So this function MUST be kept in sync with that insn
+   pattern.  PARALLEL_OP is the toplevel PARALLEL rtx, FIRST_OP is the first
+   push/pop register.  FIRST_INDEX is the element index inside PARALLEL_OP for
+   the first register push/pop rtx.  */
+
 int
-arm_attr_length_push_multi(rtx parallel_op, rtx first_op)
+arm_attr_length_pp_multi(rtx parallel_op, rtx first_op, int first_index)
 {
-  int i, regno, hi_reg;
-  int num_saves = XVECLEN (parallel_op, 0);
-
   /* ARM mode.  */
   if (TARGET_ARM)
     return 4;
@@ -27744,18 +27745,31 @@  arm_attr_length_push_multi(rtx parallel_op, rtx first_op)
   if (TARGET_THUMB1)
     return 2;
 
-  /* Thumb2 mode.  */
-  regno = REGNO (first_op);
-  hi_reg = (REGNO_REG_CLASS (regno) == HI_REGS) && (regno != LR_REGNUM);
-  for (i = 1; i < num_saves && !hi_reg; i++)
+  /* Thumb2 mode.
+     For the pattern "*push_multi", the register for the first push is kept in
+     the first UNSPEC rtx inside parallel, all other registers are kept in the
+     later USE rtxes.  For pop* pattern, each register pop is cleanly
+     represented by an (set (reg) (mem)).
+
+     So we can't always use REGNO (XEXP (input, 0)) to fetch the first register,
+     thus it's passed as argument.  Then we iterate the register list from the
+     last to the first, as the high register is usually at the end so we can
+     return earlier.  */
+
+  unsigned int regno = REGNO (first_op);
+  if ((REGNO_REG_CLASS (regno) == HI_REGS) && (regno != LR_REGNUM))
+    return 4;
+
+  int i = XVECLEN (parallel_op, 0) - 1;
+  gcc_assert (first_index >= 0 && first_index <= i);
+  for (; i > first_index; i--)
     {
       regno = REGNO (XEXP (XVECEXP (parallel_op, 0, i), 0));
-      hi_reg |= (REGNO_REG_CLASS (regno) == HI_REGS) && (regno != LR_REGNUM);
+      if (REGNO_REG_CLASS (regno) == HI_REGS && (regno != LR_REGNUM))
+	return 4;
     }
 
-  if (!hi_reg)
-    return 2;
-  return 4;
+  return 2;
 }
 
 /* Compute the number of instructions emitted by output_move_double.  */
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 7cf87ef..1e175f6 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -10488,7 +10488,7 @@ 
 ;; expressions.  For simplicity, the first register is also in the unspec
 ;; part.
 ;; To avoid the usage of GNU extension, the length attribute is computed
-;; in a C function arm_attr_length_push_multi.
+;; in a C function arm_attr_length_pp_multi.
 (define_insn "*push_multi"
   [(match_parallel 2 "multi_register_push"
     [(set (match_operand:BLK 0 "push_mult_memory_operand" "")
@@ -10530,7 +10530,7 @@ 
   }"
   [(set_attr "type" "store4")
    (set (attr "length")
-	(symbol_ref "arm_attr_length_push_multi (operands[2], operands[1])"))]
+	(symbol_ref "arm_attr_length_pp_multi (operands[2], operands[1], 0)"))]
 )
 
 (define_insn "stack_tie"
@@ -10565,7 +10565,9 @@ 
   }
   "
   [(set_attr "type" "load4")
-   (set_attr "predicable" "yes")]
+   (set_attr "predicable" "yes")
+   (set (attr "length")
+	(symbol_ref "arm_attr_length_pp_multi (operands[0], operands[3], 1)"))]
 )
 
 ;; Pop with return (as used in epilogue RTL)
@@ -10594,7 +10596,9 @@ 
   }
   "
   [(set_attr "type" "load4")
-   (set_attr "predicable" "yes")]
+   (set_attr "predicable" "yes")
+   (set (attr "length")
+	(symbol_ref "arm_attr_length_pp_multi (operands[0], operands[3], 2)"))]
 )
 
 (define_insn "*pop_multiple_with_return"
@@ -10614,7 +10618,9 @@ 
   }
   "
   [(set_attr "type" "load4")
-   (set_attr "predicable" "yes")]
+   (set_attr "predicable" "yes")
+   (set (attr "length")
+	(symbol_ref "arm_attr_length_pp_multi (operands[0], operands[2], 1)"))]
 )
 
 ;; Load into PC and return