[arm] Avoid STRD with odd register for TARGET_ARM in output_move_double
authorKyrylo Tkachov <kyrylo.tkachov@arm.com>
Fri, 29 Jun 2018 13:36:35 +0000 (13:36 +0000)
committerKyrylo Tkachov <ktkachov@gcc.gnu.org>
Fri, 29 Jun 2018 13:36:35 +0000 (13:36 +0000)
In this testcase the user forces an odd register as the starting reg for a DFmode value.
The output_move_double function tries to store that using an STRD instruction.
But for TARGET_ARM the starting register of an STRD must be an even one.
This is always the case with compiler-allocated registers for DFmode values, but the
inline assembly forced our hand here.

This patch  restricts the STRD-emitting logic in output_move_double to not avoid
odd-numbered source registers in STRD.
I'm not a fan of the whole function, we should be exposing a lot of the logic in there
to RTL rather than at the final output stage, but that would need to be fixed separately.

* config/arm/arm.c (output_move_double): Don't allow STRD instructions
if starting source register is not even.

* gcc.target/arm/arm-soft-strd-even.c: New test.

From-SVN: r262250

gcc/ChangeLog
gcc/config/arm/arm.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/arm/arm-soft-strd-even.c [new file with mode: 0644]

index 44012454e27a8f41e17074406ebf4c42bbec00b4..a76fea967828ee21a6b653af926790ac2859b8b7 100644 (file)
@@ -1,3 +1,8 @@
+2018-06-29  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+       * config/arm/arm.c (output_move_double): Don't allow STRD instructions
+       if starting source register is not even.
+
 2018-06-29  Martin Liska  <mliska@suse.cz>
 
         PR tree-optimization/86263
index c70be366ed84ca2140be3f5c4a3d8c19111a9181..f1a99588bab663e93640431de074a9f93680fa0c 100644 (file)
@@ -18463,12 +18463,18 @@ output_move_double (rtx *operands, bool emit, int *count)
       gcc_assert ((REGNO (operands[1]) != IP_REGNUM)
                   || (TARGET_ARM && TARGET_LDRD));
 
+      /* For TARGET_ARM the first source register of an STRD
+        must be even.  This is usually the case for double-word
+        values but user assembly constraints can force an odd
+        starting register.  */
+      bool allow_strd = TARGET_LDRD
+                        && !(TARGET_ARM && (REGNO (operands[1]) & 1) == 1);
       switch (GET_CODE (XEXP (operands[0], 0)))
         {
        case REG:
          if (emit)
            {
-             if (TARGET_LDRD)
+             if (allow_strd)
                output_asm_insn ("strd%?\t%1, [%m0]", operands);
              else
                output_asm_insn ("stm%?\t%m0, %M1", operands);
@@ -18476,7 +18482,7 @@ output_move_double (rtx *operands, bool emit, int *count)
          break;
 
         case PRE_INC:
-         gcc_assert (TARGET_LDRD);
+         gcc_assert (allow_strd);
          if (emit)
            output_asm_insn ("strd%?\t%1, [%m0, #8]!", operands);
          break;
@@ -18484,7 +18490,7 @@ output_move_double (rtx *operands, bool emit, int *count)
         case PRE_DEC:
          if (emit)
            {
-             if (TARGET_LDRD)
+             if (allow_strd)
                output_asm_insn ("strd%?\t%1, [%m0, #-8]!", operands);
              else
                output_asm_insn ("stmdb%?\t%m0!, %M1", operands);
@@ -18494,7 +18500,7 @@ output_move_double (rtx *operands, bool emit, int *count)
         case POST_INC:
          if (emit)
            {
-             if (TARGET_LDRD)
+             if (allow_strd)
                output_asm_insn ("strd%?\t%1, [%m0], #8", operands);
              else
                output_asm_insn ("stm%?\t%m0!, %M1", operands);
@@ -18502,7 +18508,7 @@ output_move_double (rtx *operands, bool emit, int *count)
          break;
 
         case POST_DEC:
-         gcc_assert (TARGET_LDRD);
+         gcc_assert (allow_strd);
          if (emit)
            output_asm_insn ("strd%?\t%1, [%m0], #-8", operands);
          break;
@@ -18513,8 +18519,8 @@ output_move_double (rtx *operands, bool emit, int *count)
          otherops[1] = XEXP (XEXP (XEXP (operands[0], 0), 1), 0);
          otherops[2] = XEXP (XEXP (XEXP (operands[0], 0), 1), 1);
 
-         /* IWMMXT allows offsets larger than ldrd can handle,
-            fix these up with a pair of ldr.  */
+         /* IWMMXT allows offsets larger than strd can handle,
+            fix these up with a pair of str.  */
          if (!TARGET_THUMB2
              && CONST_INT_P (otherops[2])
              && (INTVAL(otherops[2]) <= -256
@@ -18579,7 +18585,7 @@ output_move_double (rtx *operands, bool emit, int *count)
                  return "";
                }
            }
-         if (TARGET_LDRD
+         if (allow_strd
              && (REG_P (otherops[2])
                  || TARGET_THUMB2
                  || (CONST_INT_P (otherops[2])
index c7fef19358d427cfc9c0a71372f063359b951a19..9072c6d5dd638714e2aa85d7ab3e587876017f28 100644 (file)
@@ -1,3 +1,7 @@
+2018-06-29  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+       * gcc.target/arm/arm-soft-strd-even.c: New test.
+
 2018-06-29  Tom de Vries  <tdevries@suse.de>
 
        * gcc.dg/guality/pr45882.c (foo): Add line number var for breakpoint
diff --git a/gcc/testsuite/gcc.target/arm/arm-soft-strd-even.c b/gcc/testsuite/gcc.target/arm/arm-soft-strd-even.c
new file mode 100644 (file)
index 0000000..fb7317c
--- /dev/null
@@ -0,0 +1,18 @@
+/* { dg-do assemble } */
+/* { dg-require-effective-target arm_arm_ok } */
+/* { dg-options "-O2 -marm -mfloat-abi=soft" } */
+
+/* Check that we don't try to emit STRD in ARM state with
+   odd starting register.  */
+
+struct S {
+  double M0;
+} __attribute((aligned)) __attribute((packed));
+
+void bar(void *);
+
+void foo(int x, struct S y) {
+  asm("" : : : "r1", "r8", "r7", "r4");
+  y.M0 ?: bar(0);
+  bar(__builtin_alloca(8));
+}