diff mbox

[ARM,1/4] New RTL patterns for LDRD/STRD in Thumb mode

Message ID 001701cda6f8$5f19a9b0$1d4cfd10$@yorsh@arm.com
State New
Headers show

Commit Message

Greta Yorsh Oct. 10, 2012, 3:03 p.m. UTC
This patch adds define_insn patterns for LDRD and STRD in Thumb mode.

ChangeLog

gcc/

2012-09-13  Sameera Deshpande  <sameera.deshpande@arm.com>
            Greta Yorsh  <Greta.Yorsh@arm.com>

        * config/arm/arm-protos.h (offset_ok_for_ldrd_strd): New
declaration.
        (operands_ok_ldrd_strd): Likewise.
        * config/arm/arm.c (offset_ok_for_ldrd_strd): New function.
        (operands_ok_ldrd_strd): Likewise.
        * config/arm/arm.md (thumb2_ldrd, thumb2_ldrd_base): New patterns.
        (thumb2_ldrd_base_neg): Likewise.
        (thumb2_strd, thumb2_strd_base, thumb_strd_base_neg): Likewise.
        * predicates.md (ldrd_strd_offset_operand): New predicate.
        * config/arm/constraints.md (Dd): New constraint.

Comments

Richard Earnshaw Oct. 18, 2012, 1:40 p.m. UTC | #1
On 10/10/12 16:03, Greta Yorsh wrote:
> This patch adds define_insn patterns for LDRD and STRD in Thumb mode.
>
> ChangeLog
>
> gcc/
>
> 2012-09-13  Sameera Deshpande  <sameera.deshpande@arm.com>
>              Greta Yorsh  <Greta.Yorsh@arm.com>
>
>          * config/arm/arm-protos.h (offset_ok_for_ldrd_strd): New
> declaration.
>          (operands_ok_ldrd_strd): Likewise.
>          * config/arm/arm.c (offset_ok_for_ldrd_strd): New function.
>          (operands_ok_ldrd_strd): Likewise.
>          * config/arm/arm.md (thumb2_ldrd, thumb2_ldrd_base): New patterns.
>          (thumb2_ldrd_base_neg): Likewise.
>          (thumb2_strd, thumb2_strd_base, thumb_strd_base_neg): Likewise.
>          * predicates.md (ldrd_strd_offset_operand): New predicate.
>          * config/arm/constraints.md (Dd): New constraint.
>
>
> 1-thumb-patterns.patch.txt
>
>
> diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
> index c590ef4..317bca7 100644
> --- a/gcc/config/arm/arm-protos.h
> +++ b/gcc/config/arm/arm-protos.h
> @@ -116,6 +116,8 @@ 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 offset_ok_for_ldrd_strd (HOST_WIDE_INT);
> +extern bool operands_ok_ldrd_strd (rtx, rtx, rtx, HOST_WIDE_INT, bool, bool);
>   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 3fce8c4..b3a3774 100644
> --- a/gcc/config/arm/arm.c
> +++ b/gcc/config/arm/arm.c
> @@ -12123,6 +12123,75 @@ arm_pad_reg_upward (enum machine_mode mode,
>     return !BYTES_BIG_ENDIAN;
>   }
>
> +/* Returns true iff OFFSET is valid for use in an LDRD/STRD instruction,
> +   assuming that the address in the base register is word aligned.  */
> +bool
> +offset_ok_for_ldrd_strd (HOST_WIDE_INT offset)
> +{
> +  HOST_WIDE_INT max_offset;
> +
> +  /* Offset must be a multiple of 4 in Thumb mode.  */
> +  if (TARGET_THUMB2 && ((offset & 3) != 0))
> +    return false;
> +
> +  if (TARGET_THUMB2)
> +    max_offset = 1020;
> +  else if (TARGET_ARM)
> +    max_offset = 255;
> +  else
> +    gcc_unreachable ();
> +
> +  return ((offset <= max_offset) && (offset >= -max_offset));
> +}
> +
> +/* Checks whether the operands are valid for use in an LDRD/STRD instruction.
> +   Assumes that RT, RT2, and RTN are REG.  This is guaranteed by the patterns.
> +   Assumes that the address in the base register RTN is word aligned.  Pattern
> +   guarantees that both memory accesses use the same base register,
> +   the offsets are constants within the range, and the gap between the offsets is 4.
> +   If preload complete then check that registers are legal.  WBACK indicates whether
> +   address is updated.  LOAD indicates whether memory access is load or store.  */

ARM ARM terminology uses Rn for the base reg, so:

s/RTN/RN/

> +bool
> +operands_ok_ldrd_strd (rtx rt, rtx rt2, rtx rtn, HOST_WIDE_INT offset,

s/rtn/rn/


> +                       bool wback, bool load)
> +{
> +  unsigned int t, t2, n;
> +
> +  if (!reload_completed)
> +    return true;
> +
> +  if (!offset_ok_for_ldrd_strd (offset))
> +    return false;
> +
> +  t = REGNO (rt);
> +  t2 = REGNO (rt2);
> +  n = REGNO (rtn);
> +
> +  if ((TARGET_THUMB2)
> +      && ((wback && (n == t || n == t2))
> +          || (t == SP_REGNUM)
> +          || (t == PC_REGNUM)
> +          || (t2 == SP_REGNUM)
> +          || (t2 == PC_REGNUM)
> +          || (!load && (n == PC_REGNUM))
> +          || (load && (t == t2))
> +          /* Triggers Cortex-M3 LDRD errata.  */
> +          || (!wback && load && fix_cm3_ldrd && (n == t))))
> +    return false;
> +
> +  if ((TARGET_ARM)
> +      && ((wback && (n == t || n == t2))
> +          || (t2 == PC_REGNUM)
> +          || (t % 2 != 0)   /* First destination register is not even.  */
> +          || (t2 != t + 1)
> +          /* PC can be used as base register (for offset addressing only),
> +             but it is depricated.  */
> +          || (n == PC_REGNUM)))
> +    return false;
> +
> +  return true;
> +}
> +
>   
>   /* Print a symbolic form of X to the debug file, F.  */
>   static void
> diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
> index e9da56d..ed82634 100644
> --- a/gcc/config/arm/arm.md
> +++ b/gcc/config/arm/arm.md
> @@ -11472,6 +11472,99 @@
>     "
>   )
>
> +;; Patterns for LDRD/STRD in Thumb2 mode
> +
> +(define_insn "*thumb2_ldrd"
> +  [(set (match_operand:SI 0 "s_register_operand" "=r")
> +        (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
> +                         (match_operand:SI 2 "ldrd_strd_offset_operand" "Do"))))
> +   (set (match_operand:SI 3 "s_register_operand" "=r")
> +        (mem:SI (plus:SI (match_dup 1)
> +                         (match_operand:SI 4 "const_int_operand" ""))))]
> +  "TARGET_LDRD && TARGET_THUMB2
> +     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))

All these should be gated on "reload_completed" and not on the tune or 
size optimization.


> +     && ((INTVAL (operands[2]) + 4) == INTVAL (operands[4]))
> +     && (operands_ok_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 "*thumb2_ldrd_base"
> +  [(set (match_operand:SI 0 "s_register_operand" "=r")
> +        (mem:SI (match_operand:SI 1 "s_register_operand" "rk")))
> +   (set (match_operand:SI 2 "s_register_operand" "=r")
> +        (mem:SI (plus:SI (match_dup 1)
> +                         (const_int 4))))]
> +  "TARGET_LDRD && TARGET_THUMB2
> +     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
> +     && (operands_ok_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 "*thumb2_ldrd_base_neg"
> +  [(set (match_operand:SI 0 "s_register_operand" "=r")
> +	(mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
> +                         (const_int -4))))
> +   (set (match_operand:SI 2 "s_register_operand" "=r")
> +        (mem:SI (match_dup 1)))]
> +  "TARGET_LDRD && TARGET_THUMB2
> +     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
> +     && (operands_ok_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 "*thumb2_strd"
> +  [(set (mem:SI (plus:SI (match_operand:SI 0 "s_register_operand" "rk")
> +                         (match_operand:SI 1 "ldrd_strd_offset_operand" "Do")))
> +        (match_operand:SI 2 "s_register_operand" "r"))
> +   (set (mem:SI (plus:SI (match_dup 0)
> +                         (match_operand:SI 3 "const_int_operand" "")))
> +        (match_operand:SI 4 "s_register_operand" "r"))]
> +  "TARGET_LDRD && TARGET_THUMB2
> +     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
> +     && ((INTVAL (operands[1]) + 4) == INTVAL (operands[3]))
> +     && (operands_ok_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 "*thumb2_strd_base"
> +  [(set (mem:SI (match_operand:SI 0 "s_register_operand" "rk"))
> +        (match_operand:SI 1 "s_register_operand" "r"))
> +   (set (mem:SI (plus:SI (match_dup 0)
> +                         (const_int 4)))
> +        (match_operand:SI 2 "s_register_operand" "r"))]
> +  "TARGET_LDRD && TARGET_THUMB2
> +     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
> +     && (operands_ok_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 "*thumb2_strd_base_neg"
> +  [(set (mem:SI (plus:SI (match_operand:SI 0 "s_register_operand" "rk")
> +                         (const_int -4)))
> +        (match_operand:SI 1 "s_register_operand" "r"))
> +   (set (mem:SI (match_dup 0))
> +        (match_operand:SI 2 "s_register_operand" "r"))]
> +  "TARGET_LDRD && TARGET_THUMB2
> +     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
> +     && (operands_ok_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")])
> +
> +
>   ;; Load the load/store multiple patterns
>   (include "ldmstm.md")
>
> diff --git a/gcc/config/arm/constraints.md b/gcc/config/arm/constraints.md
> index b67df55..231d910 100644
> --- a/gcc/config/arm/constraints.md
> +++ b/gcc/config/arm/constraints.md
> @@ -31,7 +31,7 @@
>   ;; 'H' was previously used for FPA.
>
>   ;; The following multi-letter normal constraints have been used:
> -;; in ARM/Thumb-2 state: Da, Db, Dc, Dd, Dn, Dl, DL, Dv, Dy, Di, Dt, Dz
> +;; in ARM/Thumb-2 state: Da, Db, Dc, Dd, Dn, Dl, DL, Do, Dv, Dy, Di, Dt, Dz
>   ;; in Thumb-1 state: Pa, Pb, Pc, Pd, Pe
>   ;; in Thumb-2 state: Pj, PJ, Ps, Pt, Pu, Pv, Pw, Px, Py
>
> @@ -279,6 +279,12 @@
>         (match_test "TARGET_32BIT
>   		   && imm_for_neon_inv_logic_operand (op, GET_MODE (op))")))
>
> +(define_constraint "Do"
> + "@internal
> +  In ARM/Thumb2 state valid offset for an ldrd/strd instruction."
> + (and (match_code "const_int")
> +      (match_test "offset_ok_for_ldrd_strd (ival)")))
> +
>   (define_constraint "Dv"
>    "@internal
>     In ARM/Thumb-2 state a const_double which can be used with a VFP fconsts
> diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
> index 8ae26ca..badb68b 100644
> --- a/gcc/config/arm/predicates.md
> +++ b/gcc/config/arm/predicates.md
> @@ -137,6 +137,10 @@
>          (match_test "((unsigned HOST_WIDE_INT) INTVAL (op)) <= GET_MODE_BITSIZE (mode)
>   	&& ((unsigned HOST_WIDE_INT) INTVAL (op)) > 0")))
>
> +(define_predicate "ldrd_strd_offset_operand"
> +  (and (match_operand 0 "const_int_operand")
> +       (match_test "offset_ok_for_ldrd_strd (INTVAL (op))")))
> +
>   (define_predicate "arm_add_operand"
>     (ior (match_operand 0 "arm_rhs_operand")
>          (match_operand 0 "arm_neg_immediate_operand")))
> diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
> index f330da3..21d1aa8 100644
> --- a/gcc/config/arm/arm.c
> +++ b/gcc/config/arm/arm.c
> @@ -12130,6 +12130,9 @@ offset_ok_for_ldrd_strd (HOST_WIDE_INT offset)
>   {
>     HOST_WIDE_INT max_offset;
>
> +  if (!TARGET_LDRD)
> +    return false;
> +

This seems to be in the wrong place.  If we don't have ldrd then the 
question as to what is a valid offset is irrelevant.

>     /* Offset must be a multiple of 4 in Thumb mode.  */
>     if (TARGET_THUMB2 && ((offset & 3) != 0))
>       return false;
>
diff mbox

Patch

diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index c590ef4..317bca7 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -116,6 +116,8 @@  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 offset_ok_for_ldrd_strd (HOST_WIDE_INT);
+extern bool operands_ok_ldrd_strd (rtx, rtx, rtx, HOST_WIDE_INT, bool, bool);
 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 3fce8c4..b3a3774 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -12123,6 +12123,75 @@  arm_pad_reg_upward (enum machine_mode mode,
   return !BYTES_BIG_ENDIAN;
 }
 
+/* Returns true iff OFFSET is valid for use in an LDRD/STRD instruction,
+   assuming that the address in the base register is word aligned.  */
+bool
+offset_ok_for_ldrd_strd (HOST_WIDE_INT offset)
+{
+  HOST_WIDE_INT max_offset;
+
+  /* Offset must be a multiple of 4 in Thumb mode.  */
+  if (TARGET_THUMB2 && ((offset & 3) != 0))
+    return false;
+
+  if (TARGET_THUMB2)
+    max_offset = 1020;
+  else if (TARGET_ARM)
+    max_offset = 255;
+  else
+    gcc_unreachable ();
+
+  return ((offset <= max_offset) && (offset >= -max_offset));
+}
+
+/* Checks whether the operands are valid for use in an LDRD/STRD instruction.
+   Assumes that RT, RT2, and RTN are REG.  This is guaranteed by the patterns.
+   Assumes that the address in the base register RTN is word aligned.  Pattern
+   guarantees that both memory accesses use the same base register,
+   the offsets are constants within the range, and the gap between the offsets is 4.
+   If preload complete then check that registers are legal.  WBACK indicates whether
+   address is updated.  LOAD indicates whether memory access is load or store.  */
+bool
+operands_ok_ldrd_strd (rtx rt, rtx rt2, rtx rtn, HOST_WIDE_INT offset,
+                       bool wback, bool load)
+{
+  unsigned int t, t2, n;
+
+  if (!reload_completed)
+    return true;
+
+  if (!offset_ok_for_ldrd_strd (offset))
+    return false;
+
+  t = REGNO (rt);
+  t2 = REGNO (rt2);
+  n = REGNO (rtn);
+
+  if ((TARGET_THUMB2)
+      && ((wback && (n == t || n == t2))
+          || (t == SP_REGNUM)
+          || (t == PC_REGNUM)
+          || (t2 == SP_REGNUM)
+          || (t2 == PC_REGNUM)
+          || (!load && (n == PC_REGNUM))
+          || (load && (t == t2))
+          /* Triggers Cortex-M3 LDRD errata.  */
+          || (!wback && load && fix_cm3_ldrd && (n == t))))
+    return false;
+
+  if ((TARGET_ARM)
+      && ((wback && (n == t || n == t2))
+          || (t2 == PC_REGNUM)
+          || (t % 2 != 0)   /* First destination register is not even.  */
+          || (t2 != t + 1)
+          /* PC can be used as base register (for offset addressing only),
+             but it is depricated.  */
+          || (n == PC_REGNUM)))
+    return false;
+
+  return true;
+}
+
 
 /* Print a symbolic form of X to the debug file, F.  */
 static void
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index e9da56d..ed82634 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -11472,6 +11472,99 @@ 
   "
 )
 
+;; Patterns for LDRD/STRD in Thumb2 mode
+
+(define_insn "*thumb2_ldrd"
+  [(set (match_operand:SI 0 "s_register_operand" "=r")
+        (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
+                         (match_operand:SI 2 "ldrd_strd_offset_operand" "Do"))))
+   (set (match_operand:SI 3 "s_register_operand" "=r")
+        (mem:SI (plus:SI (match_dup 1)
+                         (match_operand:SI 4 "const_int_operand" ""))))]
+  "TARGET_LDRD && TARGET_THUMB2
+     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
+     && ((INTVAL (operands[2]) + 4) == INTVAL (operands[4]))
+     && (operands_ok_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 "*thumb2_ldrd_base"
+  [(set (match_operand:SI 0 "s_register_operand" "=r")
+        (mem:SI (match_operand:SI 1 "s_register_operand" "rk")))
+   (set (match_operand:SI 2 "s_register_operand" "=r")
+        (mem:SI (plus:SI (match_dup 1)
+                         (const_int 4))))]
+  "TARGET_LDRD && TARGET_THUMB2
+     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
+     && (operands_ok_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 "*thumb2_ldrd_base_neg"
+  [(set (match_operand:SI 0 "s_register_operand" "=r")
+	(mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
+                         (const_int -4))))
+   (set (match_operand:SI 2 "s_register_operand" "=r")
+        (mem:SI (match_dup 1)))]
+  "TARGET_LDRD && TARGET_THUMB2
+     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
+     && (operands_ok_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 "*thumb2_strd"
+  [(set (mem:SI (plus:SI (match_operand:SI 0 "s_register_operand" "rk")
+                         (match_operand:SI 1 "ldrd_strd_offset_operand" "Do")))
+        (match_operand:SI 2 "s_register_operand" "r"))
+   (set (mem:SI (plus:SI (match_dup 0)
+                         (match_operand:SI 3 "const_int_operand" "")))
+        (match_operand:SI 4 "s_register_operand" "r"))]
+  "TARGET_LDRD && TARGET_THUMB2
+     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
+     && ((INTVAL (operands[1]) + 4) == INTVAL (operands[3]))
+     && (operands_ok_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 "*thumb2_strd_base"
+  [(set (mem:SI (match_operand:SI 0 "s_register_operand" "rk"))
+        (match_operand:SI 1 "s_register_operand" "r"))
+   (set (mem:SI (plus:SI (match_dup 0)
+                         (const_int 4)))
+        (match_operand:SI 2 "s_register_operand" "r"))]
+  "TARGET_LDRD && TARGET_THUMB2
+     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
+     && (operands_ok_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 "*thumb2_strd_base_neg"
+  [(set (mem:SI (plus:SI (match_operand:SI 0 "s_register_operand" "rk")
+                         (const_int -4)))
+        (match_operand:SI 1 "s_register_operand" "r"))
+   (set (mem:SI (match_dup 0))
+        (match_operand:SI 2 "s_register_operand" "r"))]
+  "TARGET_LDRD && TARGET_THUMB2
+     && (current_tune->prefer_ldrd_strd && !optimize_function_for_size_p (cfun))
+     && (operands_ok_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")])
+
+
 ;; Load the load/store multiple patterns
 (include "ldmstm.md")
 
diff --git a/gcc/config/arm/constraints.md b/gcc/config/arm/constraints.md
index b67df55..231d910 100644
--- a/gcc/config/arm/constraints.md
+++ b/gcc/config/arm/constraints.md
@@ -31,7 +31,7 @@ 
 ;; 'H' was previously used for FPA.
 
 ;; The following multi-letter normal constraints have been used:
-;; in ARM/Thumb-2 state: Da, Db, Dc, Dd, Dn, Dl, DL, Dv, Dy, Di, Dt, Dz
+;; in ARM/Thumb-2 state: Da, Db, Dc, Dd, Dn, Dl, DL, Do, Dv, Dy, Di, Dt, Dz
 ;; in Thumb-1 state: Pa, Pb, Pc, Pd, Pe
 ;; in Thumb-2 state: Pj, PJ, Ps, Pt, Pu, Pv, Pw, Px, Py
 
@@ -279,6 +279,12 @@ 
       (match_test "TARGET_32BIT
 		   && imm_for_neon_inv_logic_operand (op, GET_MODE (op))")))
 
+(define_constraint "Do"
+ "@internal
+  In ARM/Thumb2 state valid offset for an ldrd/strd instruction."
+ (and (match_code "const_int")
+      (match_test "offset_ok_for_ldrd_strd (ival)")))
+
 (define_constraint "Dv"
  "@internal
   In ARM/Thumb-2 state a const_double which can be used with a VFP fconsts
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 8ae26ca..badb68b 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -137,6 +137,10 @@ 
        (match_test "((unsigned HOST_WIDE_INT) INTVAL (op)) <= GET_MODE_BITSIZE (mode)
 	&& ((unsigned HOST_WIDE_INT) INTVAL (op)) > 0")))
 
+(define_predicate "ldrd_strd_offset_operand"
+  (and (match_operand 0 "const_int_operand")
+       (match_test "offset_ok_for_ldrd_strd (INTVAL (op))")))
+
 (define_predicate "arm_add_operand"
   (ior (match_operand 0 "arm_rhs_operand")
        (match_operand 0 "arm_neg_immediate_operand")))
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index f330da3..21d1aa8 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -12130,6 +12130,9 @@  offset_ok_for_ldrd_strd (HOST_WIDE_INT offset)
 {
   HOST_WIDE_INT max_offset;
 
+  if (!TARGET_LDRD)
+    return false;
+
   /* Offset must be a multiple of 4 in Thumb mode.  */
   if (TARGET_THUMB2 && ((offset & 3) != 0))
     return false;