Only the 8-byte vector modes had movmisalign patterns, so for scalar
modes gimple_fold_builtin_memory_op refused to fold a small memcpy into
a plain load or store: STRICT_ALIGNMENT is set and no unaligned move was
available.  The call survived to expand and was handled either by
move_by_pieces or by alpha_expand_block_move, and in both cases the
destination or the source had to stay addressable, so it got a stack
slot.

For a 4-byte load on a BWX target this produced four ldbu, four stb into
that slot and a final ldl, which is worse than the ldq_u/extll/extlh
sequence a pre-BWX target gets, because MOVE_RATIO is 7 with BWX and
by-pieces then copies a byte at a time.  Even on a pre-BWX target the
now-dead stack slot kept a 16-byte frame alive in what is otherwise a
leaf function needing no frame at all.

Extend the movmisalign mode iterator to HImode, SImode and DImode and
derive the access size from the mode rather than hardcoding 8.  The
memcpy is then folded at gimple time into a load or a store of a
type with reduced alignment, which expands to the unaligned sequence
directly, with no stack slot, no frame, and no by-pieces byte copy.
-msafe-partial keeps emitting byte stores for the store case.

gcc/ChangeLog:

        * config/alpha/alpha.cc (alpha_expand_movmisalign): Derive the
        access size from MODE rather than assuming 8 bytes.
        * config/alpha/alpha.md (MISALIGN): New mode iterator.
        (movmisalign<mode>): Use it in place of VEC.

gcc/testsuite/ChangeLog:

        * gcc.target/alpha/movmisalign-si.c: New test.
        * gcc.target/alpha/movmisalign-si-bwx.c: New test.
        * gcc.target/alpha/movmisalign-hi-bwx.c: New test.
        * gcc.target/alpha/movmisalign-run.c: New test.
---
 gcc/config/alpha/alpha.cc                     | 11 ++-
 gcc/config/alpha/alpha.md                     | 10 ++-
 .../gcc.target/alpha/movmisalign-hi-bwx.c     | 19 +++++
 .../gcc.target/alpha/movmisalign-run.c        | 79 +++++++++++++++++++
 .../gcc.target/alpha/movmisalign-si-bwx.c     |  8 ++
 .../gcc.target/alpha/movmisalign-si.c         | 30 +++++++
 6 files changed, 151 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/alpha/movmisalign-hi-bwx.c
 create mode 100644 gcc/testsuite/gcc.target/alpha/movmisalign-run.c
 create mode 100644 gcc/testsuite/gcc.target/alpha/movmisalign-si-bwx.c
 create mode 100644 gcc/testsuite/gcc.target/alpha/movmisalign-si.c

diff --git ./gcc/config/alpha/alpha.cc ./gcc/config/alpha/alpha.cc
index f70eebc8f18..15b4a074f21 100644
--- ./gcc/config/alpha/alpha.cc
+++ ./gcc/config/alpha/alpha.cc
@@ -2471,7 +2471,7 @@ alpha_expand_mov_safe_bwa (machine_mode mode, rtx 
*operands)
 }
 
 /* Implement the movmisalign patterns.  One of the operands is a memory
-   that is not naturally aligned.  Emit instructions to load it.  */
+   that is not naturally aligned.  Emit instructions to load or store it.  */
 
 void
 alpha_expand_movmisalign (machine_mode mode, rtx *operands)
@@ -2486,7 +2486,8 @@ alpha_expand_movmisalign (machine_mode mode, rtx 
*operands)
       else
        tmp = gen_reg_rtx (mode);
 
-      alpha_expand_unaligned_load (tmp, operands[1], 8, 0, 0);
+      alpha_expand_unaligned_load (tmp, operands[1], GET_MODE_SIZE (mode),
+                                  0, 0);
       if (tmp != operands[0])
        emit_move_insn (operands[0], tmp);
     }
@@ -2496,9 +2497,11 @@ alpha_expand_movmisalign (machine_mode mode, rtx 
*operands)
        operands[1] = force_reg (mode, operands[1]);
       if (TARGET_SAFE_PARTIAL)
        alpha_expand_unaligned_store_safe_partial (operands[0], operands[1],
-                                                  8, 0, BITS_PER_UNIT);
+                                                  GET_MODE_SIZE (mode), 0,
+                                                  BITS_PER_UNIT);
       else
-       alpha_expand_unaligned_store (operands[0], operands[1], 8, 0);
+       alpha_expand_unaligned_store (operands[0], operands[1],
+                                     GET_MODE_SIZE (mode), 0);
     }
   else
     gcc_unreachable ();
diff --git ./gcc/config/alpha/alpha.md ./gcc/config/alpha/alpha.md
index a0235c61325..880654b36a2 100644
--- ./gcc/config/alpha/alpha.md
+++ ./gcc/config/alpha/alpha.md
@@ -4674,9 +4674,15 @@
 })
 
 
+; Modes for which we implement misaligned accesses with the ldq_u/stq_u
+; and extract/insert/mask instruction sequences.  QImode is excluded as
+; a byte can never be misaligned.
+
+(define_mode_iterator MISALIGN [HI SI DI V8QI V4HI V2SI])
+
 (define_expand "movmisalign<mode>"
-  [(set (match_operand:VEC 0 "nonimmediate_operand")
-        (match_operand:VEC 1 "general_operand"))]
+  [(set (match_operand:MISALIGN 0 "nonimmediate_operand")
+        (match_operand:MISALIGN 1 "general_operand"))]
   ""
 {
   alpha_expand_movmisalign (<MODE>mode, operands);
diff --git ./gcc/testsuite/gcc.target/alpha/movmisalign-hi-bwx.c 
./gcc/testsuite/gcc.target/alpha/movmisalign-hi-bwx.c
new file mode 100644
index 00000000000..b6d2d9caa7d
--- /dev/null
+++ ./gcc/testsuite/gcc.target/alpha/movmisalign-hi-bwx.c
@@ -0,0 +1,19 @@
+/* Verify that a misaligned 2-byte load on a BWX target is expanded inline
+   rather than being copied a byte at a time through a stack slot.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-safe-partial -mbwx" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+unsigned short
+load_hi (const unsigned char *p)
+{
+  unsigned short v;
+
+  __builtin_memcpy (&v, p, sizeof v);
+  return v;
+}
+
+/* { dg-final { scan-assembler-times "\\sldbu\\s" 2 } } */
+/* { dg-final { scan-assembler-not "\\sstb\\s" } } */
+/* { dg-final { scan-assembler-not "\\slda\\s\\\$30," } } */
diff --git ./gcc/testsuite/gcc.target/alpha/movmisalign-run.c 
./gcc/testsuite/gcc.target/alpha/movmisalign-run.c
new file mode 100644
index 00000000000..d98cff372c1
--- /dev/null
+++ ./gcc/testsuite/gcc.target/alpha/movmisalign-run.c
@@ -0,0 +1,79 @@
+/* Verify that misaligned loads and stores of all sizes produce correct
+   results at every alignment.  */
+
+/* { dg-do run } */
+/* { dg-options "" } */
+
+static unsigned char buf[64];
+static unsigned char ref[64];
+static unsigned char exp[64];
+
+#define DEFINE_ACCESSORS(SUFFIX, TYPE)                                 \
+  __attribute__ ((noipa)) static TYPE                                  \
+  load_##SUFFIX (const unsigned char *p)                               \
+  {                                                                    \
+    TYPE v;                                                            \
+                                                                       \
+    __builtin_memcpy (&v, p, sizeof v);                                        
\
+    return v;                                                          \
+  }                                                                    \
+                                                                       \
+  __attribute__ ((noipa)) static void                                  \
+  store_##SUFFIX (unsigned char *p, TYPE v)                            \
+  {                                                                    \
+    __builtin_memcpy (p, &v, sizeof v);                                        
\
+  }
+
+DEFINE_ACCESSORS (hi, unsigned short)
+DEFINE_ACCESSORS (si, unsigned int)
+DEFINE_ACCESSORS (di, unsigned long)
+
+#define CHECK_LOAD(SUFFIX, TYPE, OFS)                                  \
+  do                                                                   \
+    {                                                                  \
+      TYPE v;                                                          \
+                                                                       \
+      __builtin_memcpy (&v, ref + (OFS), sizeof v);                    \
+      if (load_##SUFFIX (buf + (OFS)) != v)                            \
+       __builtin_abort ();                                             \
+    }                                                                  \
+  while (0)
+
+#define CHECK_STORE(SUFFIX, TYPE, OFS, VAL)                            \
+  do                                                                   \
+    {                                                                  \
+      TYPE v = (VAL);                                                  \
+                                                                       \
+      __builtin_memcpy (buf, ref, sizeof buf);                         \
+      __builtin_memcpy (exp, ref, sizeof exp);                         \
+      store_##SUFFIX (buf + (OFS), v);                                 \
+      __builtin_memcpy (exp + (OFS), &v, sizeof v);                    \
+      if (__builtin_memcmp (buf, exp, sizeof buf) != 0)                        
\
+       __builtin_abort ();                                             \
+    }                                                                  \
+  while (0)
+
+int
+main (void)
+{
+  int i;
+
+  for (i = 0; i < (int) sizeof ref; i++)
+    ref[i] = buf[i] = i * 7 + 3;
+
+  for (i = 0; i < 16; i++)
+    {
+      CHECK_LOAD (hi, unsigned short, i);
+      CHECK_LOAD (si, unsigned int, i);
+      CHECK_LOAD (di, unsigned long, i);
+    }
+
+  for (i = 0; i < 16; i++)
+    {
+      CHECK_STORE (hi, unsigned short, i, 0x1234);
+      CHECK_STORE (si, unsigned int, i, 0xdeadbeef);
+      CHECK_STORE (di, unsigned long, i, 0x0123456789abcdef);
+    }
+
+  return 0;
+}
diff --git ./gcc/testsuite/gcc.target/alpha/movmisalign-si-bwx.c 
./gcc/testsuite/gcc.target/alpha/movmisalign-si-bwx.c
new file mode 100644
index 00000000000..5e851258d7f
--- /dev/null
+++ ./gcc/testsuite/gcc.target/alpha/movmisalign-si-bwx.c
@@ -0,0 +1,8 @@
+/* Same as movmisalign-si.c, but for a BWX target, where the copy used to be
+   made one byte at a time via a stack slot.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-safe-partial -mbwx" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+#include "movmisalign-si.c"
diff --git ./gcc/testsuite/gcc.target/alpha/movmisalign-si.c 
./gcc/testsuite/gcc.target/alpha/movmisalign-si.c
new file mode 100644
index 00000000000..4fc8d3466c1
--- /dev/null
+++ ./gcc/testsuite/gcc.target/alpha/movmisalign-si.c
@@ -0,0 +1,30 @@
+/* Verify that a misaligned 4-byte load and store are expanded inline with
+   the ldq_u/extXl/extXh and ldq_u/mskXl/insXl/stq_u sequences rather than
+   being copied a byte at a time through a stack slot.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-safe-partial" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+unsigned int
+load_si (const unsigned char *p)
+{
+  unsigned int v;
+
+  __builtin_memcpy (&v, p, sizeof v);
+  return v;
+}
+
+void
+store_si (unsigned char *p, unsigned int v)
+{
+  __builtin_memcpy (p, &v, sizeof v);
+}
+
+/* { dg-final { scan-assembler-times "\\sldq_u\\s" 4 } } */
+/* { dg-final { scan-assembler-times "\\sextll\\s" 1 } } */
+/* { dg-final { scan-assembler-times "\\sextlh\\s" 1 } } */
+/* { dg-final { scan-assembler-times "\\sstq_u\\s" 2 } } */
+/* { dg-final { scan-assembler-not "\\sldbu\\s" } } */
+/* { dg-final { scan-assembler-not "\\sstb\\s" } } */
+/* { dg-final { scan-assembler-not "\\slda\\s\\\$30," } } */
-- 
2.54.0

Reply via email to