diff mbox

[ARM,1/2] add ldm_stm_operation_p

Message ID 000101cd2237$0d408410$27c18c30$@Yorsh@arm.com
State New
Headers show

Commit Message

Greta Yorsh April 24, 2012, 4:26 p.m. UTC
Move the code of the special predicates load_multiple_operation and
store_multiple_operation into a separate function. No change in
functionality. 

gcc/ChangeLog

2012-04-24  Ian Bolton  <ian.bolton at arm.com>
            Sameera Deshpande  <sameera.deshpande at arm.com>
            Greta Yorsh  <greta.yorsh at arm.com>

        * config/arm/arm-protos.h (ldm_stm_operation_p): New declaration.
        * config/arm/arm.c (ldm_stm_operation_p): New function.
        * config/arm/predicates.md (load_multiple_operation): Update
predicate.
        (store_multiple_operation): Likewise.

Comments

Richard Earnshaw April 30, 2012, 1:43 p.m. UTC | #1
On 24/04/12 17:26, Greta Yorsh wrote:
> Move the code of the special predicates load_multiple_operation and
> store_multiple_operation into a separate function. No change in
> functionality. 
> 
> gcc/ChangeLog
> 
> 2012-04-24  Ian Bolton  <ian.bolton at arm.com>
>             Sameera Deshpande  <sameera.deshpande at arm.com>
>             Greta Yorsh  <greta.yorsh at arm.com>
> 
>         * config/arm/arm-protos.h (ldm_stm_operation_p): New declaration.
>         * config/arm/arm.c (ldm_stm_operation_p): New function.
>         * config/arm/predicates.md (load_multiple_operation): Update
> predicate.
>         (store_multiple_operation): Likewise.
> 

Thanks,  I've committed this.

R.

> 
> 
> 1-predicate.patch.txt
> 
> 
> diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
> index 900d09a..7da0e90 100644
> --- a/gcc/config/arm/arm-protos.h
> +++ b/gcc/config/arm/arm-protos.h
> @@ -62,6 +62,7 @@ extern bool arm_legitimize_reload_address (rtx *, enum machine_mode, int, int,
>  extern rtx thumb_legitimize_reload_address (rtx *, enum machine_mode, int, int,
>  					    int);
>  extern int thumb1_legitimate_address_p (enum machine_mode, rtx, int);
> +extern bool ldm_stm_operation_p (rtx, bool);
>  extern int arm_const_double_rtx (rtx);
>  extern int neg_const_double_rtx_ok_for_fpa (rtx);
>  extern int vfp3_const_double_rtx (rtx);
> diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
> index e5779ce..74f4abf 100644
> --- a/gcc/config/arm/arm.c
> +++ b/gcc/config/arm/arm.c
> @@ -10138,6 +10138,150 @@ adjacent_mem_locations (rtx a, rtx b)
>    return 0;
>  }
>  
> +/* Return true if OP is a valid load or store multiple operation.  LOAD is true
> +   for load operations, false for store operations.
> +   The pattern we are trying to match for load is:
> +     [(SET (R_d0) (MEM (PLUS (addr) (offset))))
> +      (SET (R_d1) (MEM (PLUS (addr) (offset + <reg_increment>))))
> +       :
> +       :
> +      (SET (R_dn) (MEM (PLUS (addr) (offset + n * <reg_increment>))))
> +     ]
> +     where
> +     1.  If offset is 0, first insn should be (SET (R_d0) (MEM (src_addr))).
> +     2.  REGNO (R_d0) < REGNO (R_d1) < ... < REGNO (R_dn).
> +     3.  If consecutive is TRUE, then for kth register being loaded,
> +         REGNO (R_dk) = REGNO (R_d0) + k.
> +   The pattern for store is similar.  */
> +bool
> +ldm_stm_operation_p (rtx op, bool load)
> +{
> +  HOST_WIDE_INT count = XVECLEN (op, 0);
> +  rtx reg, mem, addr;
> +  unsigned regno;
> +  HOST_WIDE_INT i = 1, base = 0, offset = 0;
> +  rtx elt;
> +  bool addr_reg_in_reglist = false;
> +  bool update = false;
> +  int reg_increment;
> +  int offset_adj;
> +
> +  reg_increment = 4;
> +  offset_adj = 0;
> +
> +  if (count <= 1
> +      || GET_CODE (XVECEXP (op, 0, offset_adj)) != SET
> +      || (load && !REG_P (SET_DEST (XVECEXP (op, 0, offset_adj)))))
> +    return false;
> +
> +  /* Check if this is a write-back.  */
> +  elt = XVECEXP (op, 0, offset_adj);
> +  if (GET_CODE (SET_SRC (elt)) == PLUS)
> +    {
> +      i++;
> +      base = 1;
> +      update = true;
> +
> +      /* The offset adjustment must be the number of registers being
> +         popped times the size of a single register.  */
> +      if (!REG_P (SET_DEST (elt))
> +          || !REG_P (XEXP (SET_SRC (elt), 0))
> +          || (REGNO (SET_DEST (elt)) != REGNO (XEXP (SET_SRC (elt), 0)))
> +          || !CONST_INT_P (XEXP (SET_SRC (elt), 1))
> +          || INTVAL (XEXP (SET_SRC (elt), 1)) !=
> +             ((count - 1 - offset_adj) * reg_increment))
> +        return false;
> +    }
> +
> +  i = i + offset_adj;
> +  base = base + offset_adj;
> +  /* Perform a quick check so we don't blow up below.  */
> +  if (count <= i)
> +    return false;
> +
> +  elt = XVECEXP (op, 0, i - 1);
> +  if (GET_CODE (elt) != SET)
> +    return false;
> +
> +  if (load)
> +    {
> +      reg = SET_DEST (elt);
> +      mem = SET_SRC (elt);
> +    }
> +  else
> +    {
> +      reg = SET_SRC (elt);
> +      mem = SET_DEST (elt);
> +    }
> +
> +  if (!REG_P (reg) || !MEM_P (mem))
> +    return false;
> +
> +  regno = REGNO (reg);
> +  addr = XEXP (mem, 0);
> +  if (GET_CODE (addr) == PLUS)
> +    {
> +      if (!CONST_INT_P (XEXP (addr, 1)))
> +	return false;
> +
> +      offset = INTVAL (XEXP (addr, 1));
> +      addr = XEXP (addr, 0);
> +    }
> +
> +  if (!REG_P (addr))
> +    return false;
> +
> +  for (; i < count; i++)
> +    {
> +      elt = XVECEXP (op, 0, i);
> +      if (GET_CODE (elt) != SET)
> +        return false;
> +
> +      if (load)
> +        {
> +          reg = SET_DEST (elt);
> +          mem = SET_SRC (elt);
> +        }
> +      else
> +        {
> +          reg = SET_SRC (elt);
> +          mem = SET_DEST (elt);
> +        }
> +
> +      if (!REG_P (reg)
> +          || GET_MODE (reg) != SImode
> +          || REGNO (reg) <= regno
> +          || !MEM_P (mem)
> +          || GET_MODE (mem) != SImode
> +          || ((GET_CODE (XEXP (mem, 0)) != PLUS
> +	       || !rtx_equal_p (XEXP (XEXP (mem, 0), 0), addr)
> +	       || !CONST_INT_P (XEXP (XEXP (mem, 0), 1))
> +	       || (INTVAL (XEXP (XEXP (mem, 0), 1)) !=
> +                   offset + (i - base) * reg_increment))
> +	      && (!REG_P (XEXP (mem, 0))
> +		  || offset + (i - base) * reg_increment != 0)))
> +        return false;
> +
> +      regno = REGNO (reg);
> +      if (regno == REGNO (addr))
> +        addr_reg_in_reglist = true;
> +    }
> +
> +  if (load)
> +    {
> +      if (update && addr_reg_in_reglist)
> +        return false;
> +
> +      /* For Thumb-1, address register is always modified - either by write-back
> +         or by explicit load.  If the pattern does not describe an update,
> +         then the address register must be in the list of loaded registers.  */
> +      if (TARGET_THUMB1)
> +        return update || addr_reg_in_reglist;
> +    }
> +
> +  return true;
> +}
> +
>  /* Return true iff it would be profitable to turn a sequence of NOPS loads
>     or stores (depending on IS_STORE) into a load-multiple or store-multiple
>     instruction.  ADD_OFFSET is nonzero if the base address register needs
> diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
> index 9171d73..0c44f47 100644
> --- a/gcc/config/arm/predicates.md
> +++ b/gcc/config/arm/predicates.md
> @@ -380,154 +380,13 @@
>  (define_special_predicate "load_multiple_operation"
>    (match_code "parallel")
>  {
> -  HOST_WIDE_INT count = XVECLEN (op, 0);
> -  unsigned dest_regno;
> -  rtx src_addr;
> -  HOST_WIDE_INT i = 1, base = 0;
> -  HOST_WIDE_INT offset = 0;
> -  rtx elt;
> -  bool addr_reg_loaded = false;
> -  bool update = false;
> -
> -  if (count <= 1
> -      || GET_CODE (XVECEXP (op, 0, 0)) != SET
> -      || !REG_P (SET_DEST (XVECEXP (op, 0, 0))))
> -    return false;
> -
> -  /* Check to see if this might be a write-back.  */
> -  if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
> -    {
> -      i++;
> -      base = 1;
> -      update = true;
> -
> -      /* Now check it more carefully.  */
> -      if (GET_CODE (SET_DEST (elt)) != REG
> -          || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
> -          || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
> -          || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4)
> -        return false;
> -    }
> -
> -  /* Perform a quick check so we don't blow up below.  */
> -  if (count <= i
> -      || GET_CODE (XVECEXP (op, 0, i - 1)) != SET
> -      || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG
> -      || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM)
> -    return false;
> -
> -  dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1)));
> -  src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0);
> -  if (GET_CODE (src_addr) == PLUS)
> -    {
> -      if (GET_CODE (XEXP (src_addr, 1)) != CONST_INT)
> -	return false;
> -      offset = INTVAL (XEXP (src_addr, 1));
> -      src_addr = XEXP (src_addr, 0);
> -    }
> -  if (!REG_P (src_addr))
> -    return false;
> -
> -  for (; i < count; i++)
> -    {
> -      elt = XVECEXP (op, 0, i);
> -
> -      if (GET_CODE (elt) != SET
> -          || GET_CODE (SET_DEST (elt)) != REG
> -          || GET_MODE (SET_DEST (elt)) != SImode
> -          || REGNO (SET_DEST (elt)) <= dest_regno
> -          || GET_CODE (SET_SRC (elt)) != MEM
> -          || GET_MODE (SET_SRC (elt)) != SImode
> -          || ((GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
> -	       || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
> -	       || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
> -	       || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != offset + (i - base) * 4)
> -	      && (!REG_P (XEXP (SET_SRC (elt), 0))
> -		  || offset + (i - base) * 4 != 0)))
> -        return false;
> -      dest_regno = REGNO (SET_DEST (elt));
> -      if (dest_regno == REGNO (src_addr))
> -        addr_reg_loaded = true;
> -    }
> -  /* For Thumb, we only have updating instructions.  If the pattern does
> -     not describe an update, it must be because the address register is
> -     in the list of loaded registers - on the hardware, this has the effect
> -     of overriding the update.  */
> -  if (update && addr_reg_loaded)
> -    return false;
> -  if (TARGET_THUMB1)
> -    return update || addr_reg_loaded;
> -  return true;
> + return ldm_stm_operation_p (op, /*load=*/true);
>  })
>  
>  (define_special_predicate "store_multiple_operation"
>    (match_code "parallel")
>  {
> -  HOST_WIDE_INT count = XVECLEN (op, 0);
> -  unsigned src_regno;
> -  rtx dest_addr;
> -  HOST_WIDE_INT i = 1, base = 0, offset = 0;
> -  rtx elt;
> -
> -  if (count <= 1
> -      || GET_CODE (XVECEXP (op, 0, 0)) != SET)
> -    return false;
> -
> -  /* Check to see if this might be a write-back.  */
> -  if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
> -    {
> -      i++;
> -      base = 1;
> -
> -      /* Now check it more carefully.  */
> -      if (GET_CODE (SET_DEST (elt)) != REG
> -          || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
> -          || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
> -          || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4)
> -        return false;
> -    }
> -
> -  /* Perform a quick check so we don't blow up below.  */
> -  if (count <= i
> -      || GET_CODE (XVECEXP (op, 0, i - 1)) != SET
> -      || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM
> -      || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG)
> -    return false;
> -
> -  src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1)));
> -  dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0);
> -
> -  if (GET_CODE (dest_addr) == PLUS)
> -    {
> -      if (GET_CODE (XEXP (dest_addr, 1)) != CONST_INT)
> -	return false;
> -      offset = INTVAL (XEXP (dest_addr, 1));
> -      dest_addr = XEXP (dest_addr, 0);
> -    }
> -  if (!REG_P (dest_addr))
> -    return false;
> -
> -  for (; i < count; i++)
> -    {
> -      elt = XVECEXP (op, 0, i);
> -
> -      if (GET_CODE (elt) != SET
> -          || GET_CODE (SET_SRC (elt)) != REG
> -          || GET_MODE (SET_SRC (elt)) != SImode
> -          || REGNO (SET_SRC (elt)) <= src_regno
> -          || GET_CODE (SET_DEST (elt)) != MEM
> -          || GET_MODE (SET_DEST (elt)) != SImode
> -          || ((GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
> -	       || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
> -	       || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
> -               || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != offset + (i - base) * 4)
> -	      && (!REG_P (XEXP (SET_DEST (elt), 0))
> -		  || offset + (i - base) * 4 != 0)))
> -        return false;
> -      src_regno = REGNO (SET_SRC (elt));
> -    }
> -
> -  return true;
> + return ldm_stm_operation_p (op, /*load=*/false);
>  })
>  
>  (define_special_predicate "multi_register_push"
diff mbox

Patch

diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 900d09a..7da0e90 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -62,6 +62,7 @@  extern bool arm_legitimize_reload_address (rtx *, enum machine_mode, int, int,
 extern rtx thumb_legitimize_reload_address (rtx *, enum machine_mode, int, int,
 					    int);
 extern int thumb1_legitimate_address_p (enum machine_mode, rtx, int);
+extern bool ldm_stm_operation_p (rtx, bool);
 extern int arm_const_double_rtx (rtx);
 extern int neg_const_double_rtx_ok_for_fpa (rtx);
 extern int vfp3_const_double_rtx (rtx);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index e5779ce..74f4abf 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -10138,6 +10138,150 @@  adjacent_mem_locations (rtx a, rtx b)
   return 0;
 }
 
+/* Return true if OP is a valid load or store multiple operation.  LOAD is true
+   for load operations, false for store operations.
+   The pattern we are trying to match for load is:
+     [(SET (R_d0) (MEM (PLUS (addr) (offset))))
+      (SET (R_d1) (MEM (PLUS (addr) (offset + <reg_increment>))))
+       :
+       :
+      (SET (R_dn) (MEM (PLUS (addr) (offset + n * <reg_increment>))))
+     ]
+     where
+     1.  If offset is 0, first insn should be (SET (R_d0) (MEM (src_addr))).
+     2.  REGNO (R_d0) < REGNO (R_d1) < ... < REGNO (R_dn).
+     3.  If consecutive is TRUE, then for kth register being loaded,
+         REGNO (R_dk) = REGNO (R_d0) + k.
+   The pattern for store is similar.  */
+bool
+ldm_stm_operation_p (rtx op, bool load)
+{
+  HOST_WIDE_INT count = XVECLEN (op, 0);
+  rtx reg, mem, addr;
+  unsigned regno;
+  HOST_WIDE_INT i = 1, base = 0, offset = 0;
+  rtx elt;
+  bool addr_reg_in_reglist = false;
+  bool update = false;
+  int reg_increment;
+  int offset_adj;
+
+  reg_increment = 4;
+  offset_adj = 0;
+
+  if (count <= 1
+      || GET_CODE (XVECEXP (op, 0, offset_adj)) != SET
+      || (load && !REG_P (SET_DEST (XVECEXP (op, 0, offset_adj)))))
+    return false;
+
+  /* Check if this is a write-back.  */
+  elt = XVECEXP (op, 0, offset_adj);
+  if (GET_CODE (SET_SRC (elt)) == PLUS)
+    {
+      i++;
+      base = 1;
+      update = true;
+
+      /* The offset adjustment must be the number of registers being
+         popped times the size of a single register.  */
+      if (!REG_P (SET_DEST (elt))
+          || !REG_P (XEXP (SET_SRC (elt), 0))
+          || (REGNO (SET_DEST (elt)) != REGNO (XEXP (SET_SRC (elt), 0)))
+          || !CONST_INT_P (XEXP (SET_SRC (elt), 1))
+          || INTVAL (XEXP (SET_SRC (elt), 1)) !=
+             ((count - 1 - offset_adj) * reg_increment))
+        return false;
+    }
+
+  i = i + offset_adj;
+  base = base + offset_adj;
+  /* Perform a quick check so we don't blow up below.  */
+  if (count <= i)
+    return false;
+
+  elt = XVECEXP (op, 0, i - 1);
+  if (GET_CODE (elt) != SET)
+    return false;
+
+  if (load)
+    {
+      reg = SET_DEST (elt);
+      mem = SET_SRC (elt);
+    }
+  else
+    {
+      reg = SET_SRC (elt);
+      mem = SET_DEST (elt);
+    }
+
+  if (!REG_P (reg) || !MEM_P (mem))
+    return false;
+
+  regno = REGNO (reg);
+  addr = XEXP (mem, 0);
+  if (GET_CODE (addr) == PLUS)
+    {
+      if (!CONST_INT_P (XEXP (addr, 1)))
+	return false;
+
+      offset = INTVAL (XEXP (addr, 1));
+      addr = XEXP (addr, 0);
+    }
+
+  if (!REG_P (addr))
+    return false;
+
+  for (; i < count; i++)
+    {
+      elt = XVECEXP (op, 0, i);
+      if (GET_CODE (elt) != SET)
+        return false;
+
+      if (load)
+        {
+          reg = SET_DEST (elt);
+          mem = SET_SRC (elt);
+        }
+      else
+        {
+          reg = SET_SRC (elt);
+          mem = SET_DEST (elt);
+        }
+
+      if (!REG_P (reg)
+          || GET_MODE (reg) != SImode
+          || REGNO (reg) <= regno
+          || !MEM_P (mem)
+          || GET_MODE (mem) != SImode
+          || ((GET_CODE (XEXP (mem, 0)) != PLUS
+	       || !rtx_equal_p (XEXP (XEXP (mem, 0), 0), addr)
+	       || !CONST_INT_P (XEXP (XEXP (mem, 0), 1))
+	       || (INTVAL (XEXP (XEXP (mem, 0), 1)) !=
+                   offset + (i - base) * reg_increment))
+	      && (!REG_P (XEXP (mem, 0))
+		  || offset + (i - base) * reg_increment != 0)))
+        return false;
+
+      regno = REGNO (reg);
+      if (regno == REGNO (addr))
+        addr_reg_in_reglist = true;
+    }
+
+  if (load)
+    {
+      if (update && addr_reg_in_reglist)
+        return false;
+
+      /* For Thumb-1, address register is always modified - either by write-back
+         or by explicit load.  If the pattern does not describe an update,
+         then the address register must be in the list of loaded registers.  */
+      if (TARGET_THUMB1)
+        return update || addr_reg_in_reglist;
+    }
+
+  return true;
+}
+
 /* Return true iff it would be profitable to turn a sequence of NOPS loads
    or stores (depending on IS_STORE) into a load-multiple or store-multiple
    instruction.  ADD_OFFSET is nonzero if the base address register needs
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 9171d73..0c44f47 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -380,154 +380,13 @@ 
 (define_special_predicate "load_multiple_operation"
   (match_code "parallel")
 {
-  HOST_WIDE_INT count = XVECLEN (op, 0);
-  unsigned dest_regno;
-  rtx src_addr;
-  HOST_WIDE_INT i = 1, base = 0;
-  HOST_WIDE_INT offset = 0;
-  rtx elt;
-  bool addr_reg_loaded = false;
-  bool update = false;
-
-  if (count <= 1
-      || GET_CODE (XVECEXP (op, 0, 0)) != SET
-      || !REG_P (SET_DEST (XVECEXP (op, 0, 0))))
-    return false;
-
-  /* Check to see if this might be a write-back.  */
-  if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
-    {
-      i++;
-      base = 1;
-      update = true;
-
-      /* Now check it more carefully.  */
-      if (GET_CODE (SET_DEST (elt)) != REG
-          || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
-          || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
-          || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4)
-        return false;
-    }
-
-  /* Perform a quick check so we don't blow up below.  */
-  if (count <= i
-      || GET_CODE (XVECEXP (op, 0, i - 1)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != REG
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != MEM)
-    return false;
-
-  dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1)));
-  src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0);
-  if (GET_CODE (src_addr) == PLUS)
-    {
-      if (GET_CODE (XEXP (src_addr, 1)) != CONST_INT)
-	return false;
-      offset = INTVAL (XEXP (src_addr, 1));
-      src_addr = XEXP (src_addr, 0);
-    }
-  if (!REG_P (src_addr))
-    return false;
-
-  for (; i < count; i++)
-    {
-      elt = XVECEXP (op, 0, i);
-
-      if (GET_CODE (elt) != SET
-          || GET_CODE (SET_DEST (elt)) != REG
-          || GET_MODE (SET_DEST (elt)) != SImode
-          || REGNO (SET_DEST (elt)) <= dest_regno
-          || GET_CODE (SET_SRC (elt)) != MEM
-          || GET_MODE (SET_SRC (elt)) != SImode
-          || ((GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
-	       || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
-	       || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
-	       || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != offset + (i - base) * 4)
-	      && (!REG_P (XEXP (SET_SRC (elt), 0))
-		  || offset + (i - base) * 4 != 0)))
-        return false;
-      dest_regno = REGNO (SET_DEST (elt));
-      if (dest_regno == REGNO (src_addr))
-        addr_reg_loaded = true;
-    }
-  /* For Thumb, we only have updating instructions.  If the pattern does
-     not describe an update, it must be because the address register is
-     in the list of loaded registers - on the hardware, this has the effect
-     of overriding the update.  */
-  if (update && addr_reg_loaded)
-    return false;
-  if (TARGET_THUMB1)
-    return update || addr_reg_loaded;
-  return true;
+ return ldm_stm_operation_p (op, /*load=*/true);
 })
 
 (define_special_predicate "store_multiple_operation"
   (match_code "parallel")
 {
-  HOST_WIDE_INT count = XVECLEN (op, 0);
-  unsigned src_regno;
-  rtx dest_addr;
-  HOST_WIDE_INT i = 1, base = 0, offset = 0;
-  rtx elt;
-
-  if (count <= 1
-      || GET_CODE (XVECEXP (op, 0, 0)) != SET)
-    return false;
-
-  /* Check to see if this might be a write-back.  */
-  if (GET_CODE (SET_SRC (elt = XVECEXP (op, 0, 0))) == PLUS)
-    {
-      i++;
-      base = 1;
-
-      /* Now check it more carefully.  */
-      if (GET_CODE (SET_DEST (elt)) != REG
-          || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
-          || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
-          || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4)
-        return false;
-    }
-
-  /* Perform a quick check so we don't blow up below.  */
-  if (count <= i
-      || GET_CODE (XVECEXP (op, 0, i - 1)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, i - 1))) != MEM
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, i - 1))) != REG)
-    return false;
-
-  src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1)));
-  dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0);
-
-  if (GET_CODE (dest_addr) == PLUS)
-    {
-      if (GET_CODE (XEXP (dest_addr, 1)) != CONST_INT)
-	return false;
-      offset = INTVAL (XEXP (dest_addr, 1));
-      dest_addr = XEXP (dest_addr, 0);
-    }
-  if (!REG_P (dest_addr))
-    return false;
-
-  for (; i < count; i++)
-    {
-      elt = XVECEXP (op, 0, i);
-
-      if (GET_CODE (elt) != SET
-          || GET_CODE (SET_SRC (elt)) != REG
-          || GET_MODE (SET_SRC (elt)) != SImode
-          || REGNO (SET_SRC (elt)) <= src_regno
-          || GET_CODE (SET_DEST (elt)) != MEM
-          || GET_MODE (SET_DEST (elt)) != SImode
-          || ((GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
-	       || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
-	       || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
-               || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != offset + (i - base) * 4)
-	      && (!REG_P (XEXP (SET_DEST (elt), 0))
-		  || offset + (i - base) * 4 != 0)))
-        return false;
-      src_regno = REGNO (SET_SRC (elt));
-    }
-
-  return true;
+ return ldm_stm_operation_p (op, /*load=*/false);
 })
 
 (define_special_predicate "multi_register_push"