===================================================================
@@ -1257,3 +1257,69 @@
"
operands[2] = GEN_INT (32 - INTVAL (operands[2]));
")
+
+(define_insn "*thumb2_ldrd"
+ [(parallel [(set (match_operand:SI 0 "s_register_operand" "")
+ (mem:SI (match_operand:SI 2 "" "")))
+ (set (match_operand:SI 1 "s_register_operand" "")
+ (mem:SI (match_operand:SI 3 "" "")))])]
+ "TARGET_THUMB2 &&
+ thumb2_ldrd_addr (operands[0], operands[1], operands[2], operands[3], 1)"
+ "*
+ {
+ rtx ldrd_addr = thumb2_ldrd_addr (operands[0], operands[1],
+ operands[2], operands[3], 1);
+ operands[4] = gen_rtx_MEM (SImode, ldrd_addr);
+ if (ldrd_addr == operands[3])
+ return \"ldrd\\t%1, %0, %4\";
+ else
+ return \"ldrd\\t%0, %1, %4\";
+ }"
+)
+
+(define_peephole2
+ [(set (match_operand:SI 0 "s_register_operand" "")
+ (mem:SI (match_operand:SI 2 "" "")))
+ (set (match_operand:SI 1 "s_register_operand" "")
+ (mem:SI (match_operand:SI 3 "" "")))]
+ "TARGET_THUMB2 &&
+ thumb2_ldrd_addr (operands[0], operands[1], operands[2], operands[3], 1)"
+ [(parallel [(set (match_operand:SI 0 "s_register_operand" "")
+ (mem:SI (match_operand:SI 2 "" "")))
+ (set (match_operand:SI 1 "s_register_operand" "")
+ (mem:SI (match_operand:SI 3 "" "")))])]
+ ""
+)
+
+(define_insn "*thumb2_strd"
+ [(parallel [(set (mem:SI (match_operand:SI 2 "" ""))
+ (match_operand:SI 0 "s_register_operand" ""))
+ (set (mem:SI (match_operand:SI 3 "" ""))
+ (match_operand:SI 1 "s_register_operand" ""))])]
+ "TARGET_THUMB2 &&
+ thumb2_ldrd_addr (operands[0], operands[1], operands[2], operands[3], 0)"
+ "*
+ {
+ rtx strd_addr = thumb2_ldrd_addr (operands[0], operands[1],
+ operands[2], operands[3], 0);
+ operands[4] = gen_rtx_MEM (SImode, strd_addr);
+ if (strd_addr == operands[3])
+ return \"strd\\t%1, %0, %4\";
+ else
+ return \"strd\\t%0, %1, %4\";
+ }"
+)
+
+(define_peephole2
+ [(set (mem:SI (match_operand:SI 2 "" ""))
+ (match_operand:SI 0 "s_register_operand" ""))
+ (set (mem:SI (match_operand:SI 3 "" ""))
+ (match_operand:SI 1 "s_register_operand" ""))]
+ "TARGET_THUMB2 &&
+ thumb2_ldrd_addr (operands[0], operands[1], operands[2], operands[3], 0)"
+ [(parallel [(set (mem:SI (match_operand:SI 2 "" ""))
+ (match_operand:SI 0 "s_register_operand" ""))
+ (set (mem:SI (match_operand:SI 3 "" ""))
+ (match_operand:SI 1 "s_register_operand" ""))])]
+ ""
+)
===================================================================
@@ -22959,4 +22959,76 @@ arm_expand_sync (enum machine_mode mode,
}
}
+/* Check if the two memory addresses can be accessed by an ldrd instruction.
+ That is they use the same base register, and the gap between constant
+ offsets should be 4. It can also be used for strd instruction.
+ If so return the lower address, otherwise return NULL. */
+rtx
+thumb2_ldrd_addr (rtx dest1, rtx dest2, rtx addr1, rtx addr2, bool ldrd)
+{
+ rtx reg1, reg2, op0, op1;
+ rtx addr = NULL;
+ HOST_WIDE_INT offset1 = 0;
+ HOST_WIDE_INT offset2 = 0;
+
+ switch (GET_CODE (addr1))
+ {
+ case REG:
+ reg1 = addr1;
+ break;
+
+ case PLUS:
+ op0 = XEXP (addr1, 0);
+ op1 = XEXP (addr1, 1);
+ if ((GET_CODE (op0) != REG) || (GET_CODE (op1) != CONST_INT))
+ return NULL;
+ reg1 = op0;
+ offset1 = INTVAL (op1);
+ break;
+
+ default:
+ return NULL;
+ }
+
+ switch (GET_CODE (addr2))
+ {
+ case REG:
+ reg2 = addr2;
+ break;
+
+ case PLUS:
+ op0 = XEXP (addr2, 0);
+ op1 = XEXP (addr2, 1);
+ if ((GET_CODE (op0) != REG) || (GET_CODE (op1) != CONST_INT))
+ return NULL;
+ reg2 = op0;
+ offset2 = INTVAL (op1);
+ break;
+
+ default:
+ return NULL;
+ }
+
+ if (reg1 != reg2)
+ return NULL;
+
+ if (ldrd && ((dest1 == dest2) || (dest1 == reg1)))
+ return NULL;
+
+ if ((offset1 + 4) == offset2)
+ addr = addr1;
+ else if ((offset2 + 4) == offset1)
+ {
+ addr = addr2;
+ offset1 = offset2;
+ }
+ else
+ return NULL;
+
+ if (((offset1 % 4) != 0) || (offset1 > 1020) || (offset1 < -1020))
+ return NULL;
+
+ return addr;
+}
+
#include "gt-arm.h"
===================================================================
@@ -149,7 +149,7 @@ extern void arm_expand_sync (enum machin
extern const char *arm_output_memory_barrier (rtx *);
extern const char *arm_output_sync_insn (rtx, rtx *);
extern unsigned int arm_sync_loop_insns (rtx , rtx *);
-
+extern rtx thumb2_ldrd_addr (rtx, rtx, rtx, rtx, bool);
extern bool arm_output_addr_const_extra (FILE *, rtx);
#if defined TREE_CODE
===================================================================
@@ -0,0 +1,20 @@
+/* { dg-options "-mthumb -O2" } */
+/* { dg-require-effective-target arm_thumb2_ok } */
+/* { dg-final { scan-assembler "ldrd" } } */
+/* { dg-final { scan-assembler "strd" } } */
+
+struct S
+{
+ void* p1;
+ void* p2;
+ void* p3;
+ void* p4;
+};
+
+void foo1(struct S* fp, struct S* otherSaveArea)
+{
+ struct S* saveA = fp - 1;
+ printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveA, otherSaveArea);
+ printf("prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+ saveA->p1, saveA->p2, saveA->p3, saveA->p4, *(unsigned int*)fp);
+}