summaryrefslogtreecommitdiff
path: root/gcc/config/arm
diff options
context:
space:
mode:
authorgretay <gretay@138bc75d-0d04-0410-961f-82ee72b054a4>2012-06-18 17:24:25 +0000
committergretay <gretay@138bc75d-0d04-0410-961f-82ee72b054a4>2012-06-18 17:24:25 +0000
commit426be8c56ddd712ef1b9959c56c5e01387412f68 (patch)
tree54f6acbbd5d6ba5320cdcd59bc25cd4deceb85cc /gcc/config/arm
parentd0e38b85acb8cef98af8da649f50ce26e55c7ec2 (diff)
downloadgcc-426be8c56ddd712ef1b9959c56c5e01387412f68.tar.gz
This patch adds new define_insn patterns for epilogue with integer
registers. The patterns can handle pop multiple with writeback and return (loading into PC directly). To handle return, the patterns use a new special predicate pop_multiple_return, that uses ldm_stm_operation_p function from a previous patch. To output assembly, the patterns use a new function arm_output_multireg_pop. This patch also adds a new function arm_emit_multi_reg_pop that emits RTL that matches the new pop patterns for integer registers. This is a helper function for epilogue expansion. It is used by a later patch. 2012-06-18 Ian Bolton <ian.bolton@arm.com> Sameera Deshpande <sameera.deshpande@arm.com> Greta Yorsh <greta.yorsh@arm.com> * config/arm/arm.md (load_multiple_with_writeback) New define_insn. (load_multiple, pop_multiple_with_writeback_and_return) Likewise. (pop_multiple_with_return, ldr_with_return) Likewise. * config/arm/predicates.md (pop_multiple_return) New special predicate. * config/arm/arm-protos.h (arm_output_multireg_pop) New declaration. * config/arm/arm.c (arm_output_multireg_pop) New function. (arm_emit_multi_reg_pop): New function. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@188739 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config/arm')
-rw-r--r--gcc/config/arm/arm-protos.h1
-rw-r--r--gcc/config/arm/arm.c158
-rw-r--r--gcc/config/arm/arm.md104
-rw-r--r--gcc/config/arm/predicates.md8
4 files changed, 271 insertions, 0 deletions
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 955f3241ade..c3d9773c547 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -156,6 +156,7 @@ extern int arm_emit_vector_const (FILE *, rtx);
extern void arm_emit_fp16_const (rtx c);
extern const char * arm_output_load_gr (rtx *);
extern const char *vfp_output_fstmd (rtx *);
+extern void arm_output_multireg_pop (rtx *, bool, rtx, bool, bool);
extern void arm_set_return_address (rtx, rtx);
extern int arm_eliminable_register (rtx);
extern const char *arm_output_shift(rtx *, int);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index d00849cb0ad..5b3e6f5add3 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -13739,6 +13739,83 @@ vfp_output_fldmd (FILE * stream, unsigned int base, int reg, int count)
}
+/* OPERANDS[0] is the entire list of insns that constitute pop,
+ OPERANDS[1] is the base register, RETURN_PC is true iff return insn
+ is in the list, UPDATE is true iff the list contains explicit
+ update of base register. */
+void
+arm_output_multireg_pop (rtx *operands, bool return_pc, rtx cond, bool reverse,
+ bool update)
+{
+ int i;
+ char pattern[100];
+ int offset;
+ const char *conditional;
+ int num_saves = XVECLEN (operands[0], 0);
+ unsigned int regno;
+ unsigned int regno_base = REGNO (operands[1]);
+
+ offset = 0;
+ offset += update ? 1 : 0;
+ offset += return_pc ? 1 : 0;
+
+ /* Is the base register in the list? */
+ for (i = offset; i < num_saves; i++)
+ {
+ regno = REGNO (XEXP (XVECEXP (operands[0], 0, i), 0));
+ /* If SP is in the list, then the base register must be SP. */
+ gcc_assert ((regno != SP_REGNUM) || (regno_base == SP_REGNUM));
+ /* If base register is in the list, there must be no explicit update. */
+ if (regno == regno_base)
+ gcc_assert (!update);
+ }
+
+ conditional = reverse ? "%?%D0" : "%?%d0";
+ if ((regno_base == SP_REGNUM) && TARGET_UNIFIED_ASM)
+ {
+ /* Output pop (not stmfd) because it has a shorter encoding. */
+ gcc_assert (update);
+ sprintf (pattern, "pop%s\t{", conditional);
+ }
+ else
+ {
+ /* Output ldmfd when the base register is SP, otherwise output ldmia.
+ It's just a convention, their semantics are identical. */
+ if (regno_base == SP_REGNUM)
+ sprintf (pattern, "ldm%sfd\t", conditional);
+ else if (TARGET_UNIFIED_ASM)
+ sprintf (pattern, "ldmia%s\t", conditional);
+ else
+ sprintf (pattern, "ldm%sia\t", conditional);
+
+ strcat (pattern, reg_names[regno_base]);
+ if (update)
+ strcat (pattern, "!, {");
+ else
+ strcat (pattern, ", {");
+ }
+
+ /* Output the first destination register. */
+ strcat (pattern,
+ reg_names[REGNO (XEXP (XVECEXP (operands[0], 0, offset), 0))]);
+
+ /* Output the rest of the destination registers. */
+ for (i = offset + 1; i < num_saves; i++)
+ {
+ strcat (pattern, ", ");
+ strcat (pattern,
+ reg_names[REGNO (XEXP (XVECEXP (operands[0], 0, i), 0))]);
+ }
+
+ strcat (pattern, "}");
+
+ if (IS_INTERRUPT (arm_current_func_type ()) && return_pc)
+ strcat (pattern, "^");
+
+ output_asm_insn (pattern, &cond);
+}
+
+
/* Output the assembly for a store multiple. */
const char *
@@ -16386,6 +16463,87 @@ emit_multi_reg_push (unsigned long mask)
return par;
}
+/* Generate and emit an insn pattern that we will recognize as a pop_multi.
+ SAVED_REGS_MASK shows which registers need to be restored.
+
+ Unfortunately, since this insn does not reflect very well the actual
+ semantics of the operation, we need to annotate the insn for the benefit
+ of DWARF2 frame unwind information. */
+static void
+arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
+{
+ int num_regs = 0;
+ int i, j;
+ rtx par;
+ rtx dwarf = NULL_RTX;
+ rtx tmp, reg;
+ bool return_in_pc;
+ int offset_adj;
+ int emit_update;
+
+ return_in_pc = (saved_regs_mask & (1 << PC_REGNUM)) ? true : false;
+ offset_adj = return_in_pc ? 1 : 0;
+ 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 + offset_adj));
+
+ if (return_in_pc)
+ {
+ tmp = ret_rtx;
+ XVECEXP (par, 0, 0) = tmp;
+ }
+
+ if (emit_update)
+ {
+ /* Increment the stack pointer, based on there being
+ num_regs 4-byte registers to restore. */
+ tmp = gen_rtx_SET (VOIDmode,
+ stack_pointer_rtx,
+ plus_constant (Pmode,
+ stack_pointer_rtx,
+ 4 * num_regs));
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ XVECEXP (par, 0, offset_adj) = tmp;
+ }
+
+ /* Now restore every reg, which may include PC. */
+ for (j = 0, i = 0; j < num_regs; i++)
+ if (saved_regs_mask & (1 << i))
+ {
+ reg = gen_rtx_REG (SImode, i);
+ tmp = gen_rtx_SET (VOIDmode,
+ 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 + offset_adj) = tmp;
+
+ /* We need to maintain a sequence for DWARF info too. As dwarf info
+ should not have PC, skip PC. */
+ if (i != PC_REGNUM)
+ dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+
+ j++;
+ }
+
+ if (return_in_pc)
+ par = emit_jump_insn (par);
+ else
+ par = emit_insn (par);
+
+ REG_NOTES (par) = dwarf;
+}
+
/* Calculate the size of the return value that is passed in registers. */
static unsigned
arm_size_return_regs (void)
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index dfa0ace5447..3fa207bab83 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -10863,6 +10863,89 @@
[(set_attr "length" "0")]
)
+;; Pop (as used in epilogue RTL)
+;;
+(define_insn "*load_multiple_with_writeback"
+ [(match_parallel 0 "load_multiple_operation"
+ [(set (match_operand:SI 1 "s_register_operand" "+rk")
+ (plus:SI (match_dup 1)
+ (match_operand:SI 2 "const_int_operand" "I")))
+ (set (match_operand:SI 3 "s_register_operand" "=rk")
+ (mem:SI (match_dup 1)))
+ ])]
+ "TARGET_32BIT && (reload_in_progress || reload_completed)"
+ "*
+ {
+ arm_output_multireg_pop (operands, /*return_pc=*/false,
+ /*cond=*/const_true_rtx,
+ /*reverse=*/false,
+ /*update=*/true);
+ return \"\";
+ }
+ "
+ [(set_attr "type" "load4")
+ (set_attr "predicable" "yes")]
+)
+
+;; Pop with return (as used in epilogue RTL)
+;;
+;; This instruction is generated when the registers are popped at the end of
+;; epilogue. Here, instead of popping the value into LR and then generating
+;; jump to LR, value is popped into PC directly. Hence, the pattern is combined
+;; with (return).
+(define_insn "*pop_multiple_with_writeback_and_return"
+ [(match_parallel 0 "pop_multiple_return"
+ [(return)
+ (set (match_operand:SI 1 "s_register_operand" "+rk")
+ (plus:SI (match_dup 1)
+ (match_operand:SI 2 "const_int_operand" "I")))
+ (set (match_operand:SI 3 "s_register_operand" "=rk")
+ (mem:SI (match_dup 1)))
+ ])]
+ "TARGET_32BIT && (reload_in_progress || reload_completed)"
+ "*
+ {
+ arm_output_multireg_pop (operands, /*return_pc=*/true,
+ /*cond=*/const_true_rtx,
+ /*reverse=*/false,
+ /*update=*/true);
+ return \"\";
+ }
+ "
+ [(set_attr "type" "load4")
+ (set_attr "predicable" "yes")]
+)
+
+(define_insn "*pop_multiple_with_return"
+ [(match_parallel 0 "pop_multiple_return"
+ [(return)
+ (set (match_operand:SI 2 "s_register_operand" "=rk")
+ (mem:SI (match_operand:SI 1 "s_register_operand" "rk")))
+ ])]
+ "TARGET_32BIT && (reload_in_progress || reload_completed)"
+ "*
+ {
+ arm_output_multireg_pop (operands, /*return_pc=*/true,
+ /*cond=*/const_true_rtx,
+ /*reverse=*/false,
+ /*update=*/false);
+ return \"\";
+ }
+ "
+ [(set_attr "type" "load4")
+ (set_attr "predicable" "yes")]
+)
+
+;; Load into PC and return
+(define_insn "*ldr_with_return"
+ [(return)
+ (set (reg:SI PC_REGNUM)
+ (mem:SI (post_inc:SI (match_operand:SI 0 "s_register_operand" "+rk"))))]
+ "TARGET_32BIT && (reload_in_progress || reload_completed)"
+ "ldr%?\t%|pc, [%0], #4"
+ [(set_attr "type" "load1")
+ (set_attr "predicable" "yes")]
+)
;; Special patterns for dealing with the constant pool
(define_insn "align_4"
@@ -11294,6 +11377,27 @@
;; Load the load/store multiple patterns
(include "ldmstm.md")
+
+;; Patterns in ldmstm.md don't cover more than 4 registers. This pattern covers
+;; large lists without explicit writeback generated for APCS_FRAME epilogue.
+(define_insn "*load_multiple"
+ [(match_parallel 0 "load_multiple_operation"
+ [(set (match_operand:SI 2 "s_register_operand" "=rk")
+ (mem:SI (match_operand:SI 1 "s_register_operand" "rk")))
+ ])]
+ "TARGET_32BIT"
+ "*
+ {
+ arm_output_multireg_pop (operands, /*return_pc=*/false,
+ /*cond=*/const_true_rtx,
+ /*reverse=*/false,
+ /*update=*/false);
+ return \"\";
+ }
+ "
+ [(set_attr "predicable" "yes")]
+)
+
;; Vector bits common to IWMMXT and Neon
(include "vec-common.md")
;; Load the Intel Wireless Multimedia Extension patterns
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 355b77281f5..6eaab3d49e9 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -368,6 +368,14 @@
/*return_pc=*/false);
})
+(define_special_predicate "pop_multiple_return"
+ (match_code "parallel")
+{
+ return ldm_stm_operation_p (op, /*load=*/true, SImode,
+ /*consecutive=*/false,
+ /*return_pc=*/true);
+})
+
(define_special_predicate "multi_register_push"
(match_code "parallel")
{