New define insn pattern for epilogue with floating point registers (DFmode)
and a new function that emits RTL for this pattern. This function is a
helper for epilogue extension. 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 (vfp_pop_multiple_with_writeback) New
define_insn.
* config/arm/predicates.md (pop_multiple_fp) New special predicate.
* config/arm/arm.c (arm_emit_vfp_multi_reg_pop): New function.
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 9093801..491ffea 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -16618,6 +16618,76 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
REG_NOTES (par) = dwarf;
}
+/* Generate and emit an insn pattern that we will recognize as a pop_multi
+ of NUM_REGS consecutive VFP regs, starting at FIRST_REG.
+
+ 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_vfp_multi_reg_pop (int first_reg, int num_regs, rtx base_reg)
+{
+ int i, j;
+ rtx par;
+ rtx dwarf = NULL_RTX;
+ rtx tmp, reg;
+
+ gcc_assert (num_regs && num_regs <= 32);
+
+ /* Workaround ARM10 VFPr1 bug. */
+ if (num_regs == 2 && !arm_arch6)
+ {
+ if (first_reg == 15)
+ first_reg--;
+
+ num_regs++;
+ }
+
+ /* We can emit at most 16 D-registers in a single pop_multi instruction, and
+ there could be up to 32 D-registers to restore.
+ If there are more than 16 D-registers, make two recursive calls,
+ each of which emits one pop_multi instruction. */
+ if (num_regs > 16)
+ {
+ arm_emit_vfp_multi_reg_pop (first_reg, 16, base_reg);
+ arm_emit_vfp_multi_reg_pop (first_reg + 16, num_regs - 16, base_reg);
+ return;
+ }
+
+ /* The parallel needs to hold num_regs SETs
+ and one SET for the stack update. */
+ par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_regs + 1));
+
+ /* Increment the stack pointer, based on there being
+ num_regs 8-byte registers to restore. */
+ tmp = gen_rtx_SET (VOIDmode,
+ base_reg,
+ plus_constant (base_reg, 8 * num_regs));
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ XVECEXP (par, 0, 0) = tmp;
+
+ /* Now show every reg that will be restored, using a SET for each. */
+ for (j = 0, i=first_reg; j < num_regs; i += 2)
+ {
+ reg = gen_rtx_REG (DFmode, i);
+
+ tmp = gen_rtx_SET (VOIDmode,
+ reg,
+ gen_frame_mem
+ (DFmode,
+ plus_constant (base_reg, 8 * j)));
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ XVECEXP (par, 0, j + 1) = tmp;
+
+ dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+
+ j++;
+ }
+
+ 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 862ccf4..98387fa 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -11042,6 +11042,41 @@
[(set_attr "type" "load1")
(set_attr "predicable" "yes")]
)
+;; Pop for floating point registers (as used in epilogue RTL)
+(define_insn "*vfp_pop_multiple_with_writeback"
+ [(match_parallel 0 "pop_multiple_fp"
+ [(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:DF 3 "arm_hard_register_operand" "")
+ (mem:DF (match_dup 1)))])]
+ "TARGET_32BIT && TARGET_HARD_FLOAT && TARGET_VFP"
+ "*
+ {
+ int num_regs = XVECLEN (operands[0], 0);
+ char pattern[100];
+ rtx op_list[2];
+ strcpy (pattern, \"fldmfdd\\t\");
+ strcat (pattern, reg_names[REGNO (SET_DEST (XVECEXP (operands[0], 0,
0)))]);
+ strcat (pattern, \"!, {\");
+ op_list[0] = XEXP (XVECEXP (operands[0], 0, 1), 0);
+ strcat (pattern, \"%P0\");
+ if ((num_regs - 1) > 1)
+ {
+ strcat (pattern, \"-%P1\");
+ op_list [1] = XEXP (XVECEXP (operands[0], 0, num_regs - 1), 0);
+ }
+
+ strcat (pattern, \"}\");
+ output_asm_insn (pattern, op_list);
+ return \"\";
+ }
+ "
+ [(set_attr "type" "load4")
+ (set_attr "conds" "unconditional")
+ (set_attr "predicable" "no")]
+)
+
;; Special patterns for dealing with the constant pool
(define_insn "align_4"
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 24dd4ea..92114bd 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -401,6 +401,14 @@
/*return_pc=*/true);
})
+(define_special_predicate "pop_multiple_fp"
+ (match_code "parallel")
+{
+ return ldm_stm_operation_p (op, /*load=*/true, DFmode,
+ /*consecutive=*/true,
+ /*return_pc=*/false);
+})
+
(define_special_predicate "multi_register_push"
(match_code "parallel")
{