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.w...@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 --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