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.
ChangeLog:
gcc
2012-05-31 Ian Bolton ian.bol...@arm.com
Sameera Deshpande sameera.deshpa...@arm.com
Greta Yorsh greta.yo...@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.
(ldm_stm_operation_p): Check SP in the register list.
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 53c2aef..7b25e37 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -156,6 +156,7 @@ extern intarm_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 4717725..9093801 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -13815,6 +13815,84 @@ 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 *
@@ -16461,6 +16539,85 @@ 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