diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 23a29c6..294f97d 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -112,6 +112,12 @@ extern bool gen_stm_seq (rtx *, int);
 extern bool gen_const_stm_seq (rtx *, int);
 extern rtx arm_gen_load_multiple (int *, int, rtx, int, rtx, HOST_WIDE_INT *);
 extern rtx arm_gen_store_multiple (int *, int, rtx, int, rtx, HOST_WIDE_INT *);
+
+extern bool bad_operands_ldrd_strd (rtx rt, rtx rt2, rtx rtn, 
+				    HOST_WIDE_INT off, bool wback, bool load);
+extern bool gen_operands_ldrd_strd (rtx *operands, int nops, bool load, bool commute);
+extern bool gen_operands_const_strd (rtx *operands, int nops);
+
 extern int arm_gen_movmemqi (rtx *);
 extern enum machine_mode arm_select_cc_mode (RTX_CODE, rtx, rtx);
 extern enum machine_mode arm_select_dominance_cc_mode (rtx, rtx,
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index f1ada6f..d52c7ae 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -264,7 +264,10 @@ static reg_class_t arm_preferred_rename_class (reg_class_t rclass);
 static unsigned int arm_autovectorize_vector_sizes (void);
 static int arm_default_branch_cost (bool, bool);
 static int arm_cortex_a5_branch_cost (bool, bool);
-
+static bool bad_mem_for_ldrd_strd(rtx addr, rtx *base, rtx *offset);
+static bool bad_offset_ldrd_strd (HOST_WIDE_INT offset);
+static bool find_free_regs_for_arm_const_strd(int len, rtx *operands, rtx base,
+					      HOST_WIDE_INT offset);
 
 /* Table of machine attributes.  */
 static const struct attribute_spec arm_attribute_table[] =
@@ -24972,4 +24975,378 @@ arm_count_output_move_double_insns (rtx *operands)
   return count;
 }
 
+/* Returns false iff OFFSET is valid for use in an LDRD/STRD instruction,
+   assuming that the address in the base register is word aligned.  */
+bool
+bad_offset_ldrd_strd (HOST_WIDE_INT offset)
+{
+  HOST_WIDE_INT max_offset;
+  if (TARGET_THUMB2)
+    max_offset = 1020;
+  else if (TARGET_ARM)
+    max_offset = 255;
+  else
+    gcc_unreachable();
+
+  return ((offset > max_offset)
+	  || (offset < -max_offset)
+	  /* Offset is not a multiple of 4.  */
+	  || ((offset & 3) != 0));   
+}
+
+/* Checks validity of operands for use in an LDRD/STRD instruction.  
+   Returns true iff the operands are invalid, under the following assumptions.
+   Assumes that rt, rt2, and rtn are REG, and off is 
+   This condition should be checked by the operand predicates 
+   in all patterns that call this function.  
+   Assumes that the address in the base register is word aligned.  */
+bool
+bad_operands_ldrd_strd (rtx rt, rtx rt2, rtx rtn, HOST_WIDE_INT offset, bool wback, bool load)
+{
+  unsigned int t, t2, n;
+
+  t = REGNO (rt);
+  t2 = REGNO (rt2);
+  n = REGNO (rtn);
+
+  if (bad_offset_ldrd_strd(offset))
+    return true;
+ 
+  if (TARGET_THUMB2)
+    return ((wback && (n == t || n == t2))
+	    || (t == SP_REGNUM)
+	    || (t == PC_REGNUM)
+	    || (t2 == SP_REGNUM)
+	    || (t2 == PC_REGNUM)
+	    || (load && (t == t2))
+	    /* Triggers Cortex-M3 LDRD errata.  */
+	    || (!wback && load && fix_cm3_ldrd && (n == t)));
+  else if (TARGET_ARM)
+    return ((wback && (n == t || n == t2))
+	    || (t2 == PC_REGNUM)
+	    || ((t % 2) != 0)   /* First destination register is not even.  */
+	    || (t2 != t + 1)
+	    /* PC be used as base register for offset addressing only,
+	       but it is depricated.  */
+	    || (n == PC_REGNUM));
+  else
+    gcc_unreachable();
+}
+
+/* Helper for gen_operands_ldrd_strd. 
+   Returns true iff the memory operand ADDR is 
+   an immediate offset from the base register and is not volatile. 
+   Sets BASE and OFFSET accordingly.  */
+bool
+bad_mem_for_ldrd_strd(rtx addr, rtx *base, rtx *offset)
+{
+  /* Convert a subreg of mem into mem itself.  */
+  if (GET_CODE (addr) == SUBREG)
+    addr = alter_subreg (&addr);
+  gcc_assert (GET_CODE (addr) == MEM);
+
+  /* Don't modify volatile memory accesses.  */
+  if (MEM_VOLATILE_P (addr))
+    return true;
+ 
+  *offset = const0_rtx;
+
+  addr = XEXP (addr, 0);
+  if (GET_CODE (addr) == REG)
+    {
+      *base = addr;
+      return false;
+    }
+  else if (GET_CODE (addr) == PLUS)
+    {
+      *base = XEXP (addr, 0);
+      *offset = XEXP (addr, 1);
+      if ((GET_CODE (*base) != REG) || (GET_CODE (*offset) != CONST_INT))
+	return true;
+      return false;
+    }
+  return true;
+}
+
+/* Called from a peephole2 to replace two word-size loads/stores 
+   with a single LDRD/STRD instruction.
+   OPERANDS are the operands found by the peephole matcher;
+   LEN indicates how many insns are matched by the pattern, 
+   and used for checking dead registers.
+   COMMUTE indicates that register operands may be reordered.
+   OPERANDS[0,1] are register operands
+   OPERANDS[2,3] are memory operands.
+   Returns true iff we can generate a new instruction sequence.
+   That is, both accesses use the same base register and 
+   the gap between constant offsets is 4.     
+   This function may reorder its operands to match ldrd/strd RTL templates.  */
+bool
+gen_operands_ldrd_strd (rtx *operands, int len, bool load, bool commute)
+{  
+  int nops = 2;
+  HOST_WIDE_INT offsets[2], offset;
+  rtx base;
+  rtx cur_base, cur_offset, tmp;
+  int i, gap;
+
+  gcc_assert (len >= 2);
+
+  /* Check that the memory references are immediate offsets 
+     from the same base register. 
+     Extract the base register, the destination registers, 
+     and the corresponding memory offsets.  */
+  for (i = 0; i < nops; i++)
+    { 
+      if (bad_mem_for_ldrd_strd(operands[nops+i], &cur_base, &cur_offset)) return false;
+      if (i == 0) 
+	base = cur_base;
+      else if ( REGNO(base) != REGNO (cur_base)) 
+	return false;
+
+      offsets[i] = INTVAL (cur_offset);
+      if (GET_CODE (operands[i]) == SUBREG)
+	operands[i] = SUBREG_REG (operands[i]);
+    }
+    
+  /* Make sure there is no dependency between the individual loads.  */
+  if (load && (REGNO(operands[0]) == REGNO(base))) return false; /* RAW */
+  if (load && (REGNO(operands[0]) == REGNO(operands[1]))) return false; /* WAW */
+
+  /* Make sure the instructions are ordered with lower memory access first.  */
+  if (offsets[0] > offsets[1])
+    {      
+      gap = (offsets[0] - offsets[1]);
+      offset = offsets[1];
+
+      /* Swap the instructions such that lower memory is accessed first.  */
+      tmp = operands[0];
+      operands[0] = operands[1];
+      operands[1] = tmp; 
+
+      tmp = operands[2];
+      operands[2] = operands[3];
+      operands[3] = tmp; 
+    }
+  else 
+    {
+      gap = (offsets[1] - offsets[0]);
+      offset = offsets[0];
+    }
+  
+  /* Make sure accesses are to consecutive memory locations.  */
+  if (gap != 4) return false;
+
+  /* Make sure we generate legal instructions.  */
+  if (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, load))
+    return true;
+
+  if (commute) 
+    {      
+      /* Try reordering registers. */
+      tmp = operands[0];
+      operands[0] = operands[1];
+      operands[1] = tmp; 
+      if (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, load))
+	return true;
+    }
+  return false;
+}
+
+/* Called from a peephole2 replace a pair of stores that are
+   preceded by constant loads into an STRD instruction.  
+   OPERANDS are the operands found by the peephole matcher; 
+   NOPS indicates how many separate stores we are trying to combine; 
+   there are 2 * NOPS instructions in the peephole.
+   Returns true iff we could generate a new instruction.  */
+/* This uses peephole2's ability to allocate free registers.  */
+/* Assumes that the constants are different. Other optimizations,
+   applied before peephole, should have taken care of 
+   the case in which constants are the same.  */
+bool
+gen_operands_const_strd (rtx *operands, int len)
+{
+  int nops = 2;
+  HOST_WIDE_INT offsets[2], offset;
+  rtx base;
+  rtx cur_base, cur_offset, tmp;
+  int i, gap;
+  HARD_REG_SET regset;
+
+  CLEAR_HARD_REG_SET (regset);
+
+  /* Check that the memory references are immediate offsets 
+     from the same base register. 
+     Extract the base register, the destination registers, 
+     and the corresponding memory offsets.  */
+  for (i = 0; i < nops; i++)
+    { 
+      if (bad_mem_for_ldrd_strd(operands[nops+i], &cur_base, &cur_offset)) return false;
+      if (i == 0) 
+	{	  
+	  base = cur_base;	  
+	}
+      else if ( REGNO(base) != REGNO (cur_base)) 
+	return false;
+
+      offsets[i] = INTVAL (cur_offset);
+      if (GET_CODE (operands[i]) == SUBREG)
+	operands[i] = SUBREG_REG (operands[i]);
+    }
+  
+  /* If the same input register is used in both stores, 
+     try to find a free register.  */
+  if (REGNO(operands[0]) == REGNO(operands[1]))
+    {     
+      /* We might choice a bad register here and fix it later.  */
+      rtx reg = peep2_find_free_register (0, 4,
+					TARGET_THUMB1 ? "l" : "r",
+					SImode, &regset);
+      if (reg == NULL_RTX)
+	return false;
+      
+      /* Use the new register in the first load to ensure that
+	 if the original input register is not dead after peephole,
+	 then it will have the correct constant value.  */
+      operands[0] = reg;
+    }
+
+  /* Make sure the instructions are ordered with lower memory access first.  */
+  if (offsets[0] > offsets[1])
+    {      
+      gap = (offsets[0] - offsets[1]);
+      offset = offsets[1];
+
+      /* Swap the instructions such that lower memory is accessed first.  */
+      tmp = operands[0];
+      operands[0] = operands[1];
+      operands[1] = tmp; 
+
+      tmp = operands[2];
+      operands[2] = operands[3];
+      operands[3] = tmp; 
+
+      tmp = operands[4];
+      operands[4] = operands[5];
+      operands[5] = tmp; 
+    }
+  else 
+    {
+      gap = (offsets[1] - offsets[0]);
+      offset = offsets[0];
+    }
+  
+  /* Make sure accesses are to consecutive memory locations.  */
+  if (gap != 4) return false;
+
+  /* If the offset is illegal, we don't try to fix it. 
+     (We can fix it if the base register is dead, but it's not worth the trouble.) */
+  if (bad_offset_ldrd_strd (offset))
+    return false;
+
+  /* Is it a leagl STRD?  */
+  if (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, false))
+    return true;
+  
+  /* This STRD is illegal because of the input registers it uses.
+     In Thumb state, where registers are almost unconstrained, 
+     there is little hope to fix it. In ARM state, if an input register is dead,
+     we try to replace it by an appropriate consecutive register.  */
+  if (TARGET_THUMB2)
+    return false;
+  else if (TARGET_ARM)
+    return find_free_regs_for_arm_const_strd (len, operands, base, offset);
+  else
+    gcc_unreachable();
+}
+
+/* 
+   Called by peephole2 patterns to turn two LDRs into an LDRD.
+   Returns true iff created a legal STRD for ARM by changing the input registers. 
+   If input registers are dead after current insn + LEN, they can be reordered
+   or replaced by other registers that are free from current insn for LEN insns.  */
+bool
+find_free_regs_for_arm_const_strd(int len, rtx *operands, rtx base, HOST_WIDE_INT offset)
+{
+  /* Who is dead?  */
+  bool dead[2];
+  int i;
+  unsigned int t;
+  rtx tmp;
+  HARD_REG_SET regset;
+
+  for (i = 0; i < 2; i++)
+    dead[i] = peep2_reg_dead_p (len, operands[i]); 
+
+  if ((!dead[0]) && (!dead[1]))
+    return false;
+
+  /* If both are dead, we can reorder them or replace them with free registers.  */
+  if (dead[0] && dead[1])
+    {
+      /* Try to reorder the input registers. */
+      if (!bad_operands_ldrd_strd (operands[1], operands[0], base, offset, false, false))
+	{
+	  tmp = operands[0];
+	  operands[0] = operands[1];
+	  operands[1] = tmp;
+	  return true;
+	}
+      
+      /* Try to find a free DI register.  */
+      CLEAR_HARD_REG_SET (regset);
+      add_to_hard_reg_set (&regset, SImode, REGNO(operands[0]));
+      add_to_hard_reg_set (&regset, SImode, REGNO(operands[1]));
+      while (true)
+	{
+	  tmp = peep2_find_free_register (0, len,
+					  TARGET_THUMB1 ? "l" : "r",
+					  DImode, &regset);
+	  if (tmp == NULL_RTX)
+	    return false;
+
+	  /* DREG must be an even-numbered register in DImode. 
+	     Split it into SI registers. */
+	  t = REGNO(tmp);
+	  operands[0] = gen_rtx_REG(SImode, t);
+	  operands[1] = gen_rtx_REG(SImode, t+1);
+	  gcc_assert ((REGNO(operands[0]) % 2) == 0);
+	  gcc_assert (operands[0] != NULL_RTX);
+	  gcc_assert (operands[1] != NULL_RTX);
+	  
+	  return (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, false));
+	}
+      gcc_unreachable();
+    }
+   
+  /* One of the input registers is dead, the other is live. 
+     Try to replace the dead register with a free register 
+     adjacent to the live register.  */  
+  if (dead[0])
+    {
+      t = REGNO(operands[1]);
+      if ((t % 2) == 0)
+	return false;
+      t = t - 1;
+      operands[0] = gen_rtx_REG(SImode, t);
+    }
+  else if (dead[1])
+    {
+      t = REGNO(operands[0]);
+      if ((t % 2) != 0)
+	return false;
+      t = t + 1;
+      operands[1] = gen_rtx_REG(SImode, t);
+    }
+  else gcc_unreachable();
+
+  /* Check if register number t is free. Is there a better way to check it? */
+  SET_HARD_REG_SET(regset);
+  remove_from_hard_reg_set (&regset, SImode, t);
+  tmp = peep2_find_free_register (0, 4, TARGET_THUMB1 ? "l" : "r", 
+				  SImode, &regset);
+  if (tmp == NULL_RTX) return false;
+  gcc_assert (REGNO(tmp) == t);
+
+  return (!bad_operands_ldrd_strd (operands[0], operands[1], base, offset, false, false));
+}
 #include "gt-arm.h"
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index a78ba88..d812bbf 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -11292,8 +11292,12 @@
   "
 )
 
+;; Load/store double-words patterns.
+(include "ldrdstrd0.md")
+
 ;; Load the load/store multiple patterns
 (include "ldmstm.md")
+
 ;; Load the FPA co-processor patterns
 (include "fpa.md")
 ;; Load the Maverick co-processor patterns
diff --git a/gcc/config/arm/constraints.md b/gcc/config/arm/constraints.md
index d8ce982..3c24c6d 100644
--- a/gcc/config/arm/constraints.md
+++ b/gcc/config/arm/constraints.md
@@ -22,6 +22,7 @@
 ;; - in ARM/Thumb-2 state: f, t, v, w, x, y, z
 ;; - in Thumb state: h, b
 ;; - in both states: l, c, k
+;; - in ARM state with LDRD support: q
 ;; In ARM state, 'l' is an alias for 'r'
 
 ;; The following normal constraints have been used:
@@ -90,6 +91,9 @@
 (define_register_constraint "k" "STACK_REG"
  "@internal The stack register.")
 
+(define_register_constraint "q" "TARGET_ARM && TARGET_LDRD ? CORE_REGS : GENERAL_REGS"
+ "@internal In ARM state with LDRD support, core registers, otherwise general registers.")
+
 (define_register_constraint "b" "TARGET_THUMB ? BASE_REGS : NO_REGS"
  "@internal
   Thumb only.  The union of the low registers and the stack register.")
diff --git a/gcc/config/arm/ldrdstrd0.md b/gcc/config/arm/ldrdstrd0.md
new file mode 100644
index 0000000..6f1bf74
--- /dev/null
+++ b/gcc/config/arm/ldrdstrd0.md
@@ -0,0 +1,164 @@
+(define_insn "*ldrd_0"
+  [(set (match_operand:SI 0 "arm_hard_core_register_operand" "=q")
+        (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
+                         (match_operand:SI 2 "const_int_operand" ""))))
+   (set (match_operand:SI 3 "arm_hard_core_register_operand" "=q")
+        (mem:SI (plus:SI (match_dup 1)
+                         (match_operand:SI 4 "const_int_operand" ""))))]
+  "TARGET_LDRD && TARGET_THUMB2 
+     && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)
+     && ((INTVAL (operands[2]) + 4) == INTVAL (operands[4]))
+     && (!bad_operands_ldrd_strd (operands[0], operands[3], 
+                                  operands[1], INTVAL (operands[2]), 
+                                  false, true))"
+  "ldrd%?\t%0, %3, [%1, %2]"
+  [(set_attr "type" "load2")
+   (set_attr "predicable" "yes")])
+
+(define_insn "*ldrd_base_0"
+  [(set (match_operand:SI 0 "arm_hard_core_register_operand" "=q")
+        (mem:SI (match_operand:SI 1 "s_register_operand" "rk")))
+   (set (match_operand:SI 2 "arm_hard_core_register_operand" "=q")
+        (mem:SI (plus:SI (match_dup 1)
+                         (const_int 4))))]
+  "TARGET_LDRD && TARGET_THUMB2 
+     && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)
+     && (!bad_operands_ldrd_strd (operands[0], operands[2], 
+                                  operands[1], 0, false, true))"
+  "ldrd%?\t%0, %2, [%1]"
+  [(set_attr "type" "load2")
+   (set_attr "predicable" "yes")])
+
+(define_insn "*ldrd_base_neg_0"
+  [(set (match_operand:SI 0 "arm_hard_core_register_operand" "=q")
+	(mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
+                         (const_int -4))))
+   (set (match_operand:SI 2 "arm_hard_core_register_operand" "=q")
+        (mem:SI (match_dup 1)))]
+  "TARGET_LDRD && TARGET_THUMB2 
+     && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)
+     && (!bad_operands_ldrd_strd (operands[0], operands[2], 
+                                  operands[1], -4, false, true))"
+  "ldrd%?\t%0, %2, [%1, #-4]"
+  [(set_attr "type" "load2")
+   (set_attr "predicable" "yes")])
+
+(define_insn "*strd_0"
+  [(set (mem:SI (plus:SI (match_operand:SI 0 "s_register_operand" "rk")
+                         (match_operand:SI 1 "const_int_operand" "")))
+        (match_operand:SI 2 "arm_hard_core_register_operand" "q"))
+   (set (mem:SI (plus:SI (match_dup 0)
+                         (match_operand:SI 3 "const_int_operand" "")))
+        (match_operand:SI 4 "arm_hard_core_register_operand" "q"))]
+  "TARGET_LDRD && TARGET_THUMB2 
+     && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)
+     && ((INTVAL (operands[1]) + 4) == INTVAL (operands[3]))
+     && (!bad_operands_ldrd_strd (operands[2], operands[4], 
+                                  operands[0], INTVAL (operands[1]), 
+                                  false, false))"
+  "strd%?\t%2, %4, [%0, %1]"
+  [(set_attr "type" "store2")
+   (set_attr "predicable" "yes")])
+
+(define_insn "*strd_base_0"
+  [(set (mem:SI (match_operand:SI 0 "s_register_operand" "rk"))
+        (match_operand:SI 1 "arm_hard_core_register_operand" "q"))
+   (set (mem:SI (plus:SI (match_dup 0)
+                         (const_int 4))) 
+        (match_operand:SI 2 "arm_hard_core_register_operand" "q"))]
+  "TARGET_LDRD && TARGET_THUMB2 
+     && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)
+     && (!bad_operands_ldrd_strd (operands[1], operands[2], 
+                                  operands[0], 0, false, false))"
+  "strd%?\t%1, %2, [%0]"
+  [(set_attr "type" "store2")
+   (set_attr "predicable" "yes")])
+
+(define_insn "*strd_base_neg_0"
+  [(set (mem:SI (plus:SI (match_operand:SI 0 "s_register_operand" "rk")
+                         (const_int -4)))
+        (match_operand:SI 1 "arm_hard_core_register_operand" "q"))
+   (set (mem:SI (match_dup 0))
+        (match_operand:SI 2 "arm_hard_core_register_operand" "q"))]
+  "TARGET_LDRD && TARGET_THUMB2 
+     && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)
+     && (!bad_operands_ldrd_strd (operands[1], operands[2], 
+                                  operands[0], -4, false, false))"
+  "strd%?\t%1, %2, [%0, #-4]"
+  [(set_attr "type" "store2")
+   (set_attr "predicable" "yes")])
+
+;; The following peephole optimizations identify consecutive memory accesses,
+;; and try to rearrange the operands to enable generation of ldrd/strd.
+
+(define_peephole2 ; ldrd
+  [(set (match_operand:SI 0 "arm_hard_core_register_operand" "")
+        (match_operand:SI 2 "memory_operand" ""))
+   (set (match_operand:SI 1 "arm_hard_core_register_operand" "")
+        (match_operand:SI 3 "memory_operand" ""))]
+  "TARGET_LDRD && TARGET_THUMB2 
+     && ((current_tune->prefer_ldrd_strd && !optimize_size)
+        || 0)"
+  [(parallel [(set (match_dup 0) (match_dup 2))
+	      (set (match_dup 1) (match_dup 3))])]
+{
+  if (!gen_operands_ldrd_strd (operands, 2, true, false))
+    FAIL;
+})
+
+(define_peephole2 ; strd
+  [(set (match_operand:SI 2 "memory_operand" "")
+	(match_operand:SI 0 "arm_hard_core_register_operand" ""))
+   (set (match_operand:SI 3 "memory_operand" "")
+	(match_operand:SI 1 "arm_hard_core_register_operand" ""))]
+  "TARGET_LDRD && TARGET_THUMB2 
+     && ((current_tune->prefer_ldrd_strd && !optimize_size)
+        || 0)"
+  [(parallel [(set (match_dup 2) (match_dup 0))
+	      (set (match_dup 3) (match_dup 1))])]
+{
+  if (!gen_operands_ldrd_strd (operands, 2, false, false))
+    FAIL;
+})
+
+;; The following peepholes reorder registers to enable LDRD/STRD.
+(define_peephole2 ; strd of constants for thumb
+  [(set (match_operand:SI 0 "s_register_operand" "")
+        (match_operand:SI 4 "const_int_operand" ""))
+   (set (match_operand:SI 2 "memory_operand" "")
+        (match_dup 0))
+   (set (match_operand:SI 1 "s_register_operand" "")
+        (match_operand:SI 5 "const_int_operand" ""))
+   (set (match_operand:SI 3 "memory_operand" "")
+        (match_dup 1))]
+ "TARGET_LDRD && TARGET_THUMB2
+  && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)"
+  [(set (match_dup 0) (match_dup 4))
+   (set (match_dup 1) (match_dup 5))
+   (parallel [(set (match_dup 2) (match_dup 0))
+	      (set (match_dup 3) (match_dup 1))])]
+{
+  if (!gen_operands_const_strd (operands, 4))
+    FAIL;
+})
+
+(define_peephole2 ; strd of constants for thumb
+  [(set (match_operand:SI 0 "s_register_operand" "")
+        (match_operand:SI 4 "const_int_operand" ""))
+   (set (match_operand:SI 1 "s_register_operand" "")
+        (match_operand:SI 5 "const_int_operand" ""))
+   (set (match_operand:SI 2 "memory_operand" "")
+        (match_dup 0))
+   (set (match_operand:SI 3 "memory_operand" "")
+        (match_dup 1))]
+ "TARGET_LDRD && TARGET_THUMB2
+  && ((current_tune->prefer_ldrd_strd && !optimize_size) || 0)"
+  [(set (match_dup 0) (match_dup 4))
+   (set (match_dup 1) (match_dup 5))
+   (parallel [(set (match_dup 2) (match_dup 0))
+	      (set (match_dup 3) (match_dup 1))])]
+{
+  if (!gen_operands_const_strd (operands, 4))
+     FAIL;
+})
+
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 92eb004..be13326 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -38,6 +38,13 @@
   return REGNO (op) < FIRST_PSEUDO_REGISTER;
 })
 
+;; Hard core register (non vpf, non pseudo)
+(define_predicate "arm_hard_core_register_operand"
+  (match_code "reg")
+{
+ return (REGNO (op) <= LAST_ARM_REGNUM);
+})
+
 ;; A low register.
 (define_predicate "low_register_operand"
   (and (match_code "reg")
diff --git a/gcc/testsuite/gcc.target/arm/pr40457-1.c b/gcc/testsuite/gcc.target/arm/pr40457-1.c
index 815fd38..990bc49 100644
--- a/gcc/testsuite/gcc.target/arm/pr40457-1.c
+++ b/gcc/testsuite/gcc.target/arm/pr40457-1.c
@@ -1,5 +1,6 @@
-/* { dg-options "-Os" }  */
+/* { dg-options "-march=armv7-a -Os" }  */
 /* { dg-do compile } */
+/* { dg-require-effective-target arm_thumb2_ok } */
 
 int bar(int* p)
 {
@@ -7,4 +8,4 @@ int bar(int* p)
   return x;
 }
 
-/* { dg-final { scan-assembler "ldm" } } */
+/* { dg-final { scan-assembler "ldrd|ldm" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pr40457-2.c b/gcc/testsuite/gcc.target/arm/pr40457-2.c
index 187f7bf..acf9f71 100644
--- a/gcc/testsuite/gcc.target/arm/pr40457-2.c
+++ b/gcc/testsuite/gcc.target/arm/pr40457-2.c
@@ -1,5 +1,6 @@
-/* { dg-options "-O2" }  */
+/* { dg-options "-march=armv7-a -O2" }  */
 /* { dg-do compile } */
+/* { dg-require-effective-target arm_thumb2_ok } */
 
 void foo(int* p)
 {
@@ -7,4 +8,4 @@ void foo(int* p)
   p[1] = 0;
 }
 
-/* { dg-final { scan-assembler "stm" } } */
+/* { dg-final { scan-assembler "strd|stm" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pr40457-3.c b/gcc/testsuite/gcc.target/arm/pr40457-3.c
index 9bd5a17..e7098cb 100644
--- a/gcc/testsuite/gcc.target/arm/pr40457-3.c
+++ b/gcc/testsuite/gcc.target/arm/pr40457-3.c
@@ -1,5 +1,6 @@
-/* { dg-options "-Os" }  */
+/* { dg-options "-march=armv7-a -Os" }  */
 /* { dg-do compile } */
+/* { dg-require-effective-target arm_thumb2_ok } */
 
 void foo(int* p)
 {
@@ -7,4 +8,4 @@ void foo(int* p)
   p[1] = 0;
 }
 
-/* { dg-final { scan-assembler "stm" } } */
+/* { dg-final { scan-assembler "strd|stm" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pr45335-2.c b/gcc/testsuite/gcc.target/arm/pr45335-2.c
new file mode 100644
index 0000000..b519580
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr45335-2.c
@@ -0,0 +1,10 @@
+/* { dg-options "-march=armv7-a -O2" }  */
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_thumb2_ok } */
+/* { dg-final { scan-assembler "stm|strd" } } */
+void foo(int a, int b, int* p)
+{
+  p[2] = a;
+  p[3] = b;
+}
+
diff --git a/gcc/testsuite/gcc.target/arm/pr45335-3.c b/gcc/testsuite/gcc.target/arm/pr45335-3.c
new file mode 100644
index 0000000..cff58d2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr45335-3.c
@@ -0,0 +1,13 @@
+/* { dg-options "-march=armv7-a -O2" }  */
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_thumb2_ok } */
+/* { dg-final { scan-assembler "ldrd|ldm" } } */
+int foo(int a, int b, int* p, int *q)
+{
+  a = p[2] + p[3];
+  *q = a;
+  *p = a;
+  return a;
+}
+
+
diff --git a/gcc/testsuite/gcc.target/arm/pr45335.c b/gcc/testsuite/gcc.target/arm/pr45335.c
new file mode 100644
index 0000000..d9aa6c7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr45335.c
@@ -0,0 +1,23 @@
+/* { dg-options "-O2 -march=armv7-a" } */
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_thumb2_ok } */
+/* { dg-final { scan-assembler "ldm|ldrd" } } */
+/* { dg-final { scan-assembler "stm|strd" } } */
+
+struct S
+{
+    void* p1;
+    void* p2;
+    void* p3;
+    void* p4;
+};
+
+extern printf(char*, ...);
+
+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);
+}
