Index: thumb2.md
===================================================================
--- thumb2.md   (revision 163363)
+++ thumb2.md   (working copy)
@@ -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" ""))])]
+  ""
+)
Index: arm.c
===================================================================
--- arm.c       (revision 163363)
+++ arm.c       (working copy)
@@ -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"
Index: arm-protos.h
===================================================================
--- arm-protos.h        (revision 163363)
+++ arm-protos.h        (working copy)
@@ -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


Index: pr45335.c
===================================================================
--- pr45335.c   (revision 0)
+++ pr45335.c   (revision 0)
@@ -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);
+}
