From: Kyrylo Tkachov <[email protected]>

The patterns that insert a scalar into element 0 of a zero SVE vector
accept the scalar only in an FP register.  A scalar in a general
register needs an extra move:

        fmov    s0, w0
        fmov    s0, s0

and, for 8-bit elements:

        dup     v31.8b, w0
        fmov    h0, h31
        and     z0.h, z0.h, #0xff

Add alternatives that take the scalar from a general register.  FMOV
from a general register also clears the rest of the vector.

Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk?
Thanks,
Kyrill

Cc: Tamar Christina <[email protected]>
Cc: Wilco Dijkstra <[email protected]>

gcc/ChangeLog:

        * config/aarch64/aarch64-sve.md
        (*aarch64_vec_shl_insert_into_zero_<mode>): Add an alternative for
        general registers.  Give the instruction type per alternative.
        (*aarch64_vec_shl_insert_into_zero_vnx16qi): Likewise.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/sve/copsi.c (func_init3): Update expected
        assembly.
        * gcc.target/aarch64/sve/insr-zero-gpr.c: New test.
        * gcc.target/aarch64/sve/insr-zero-gpr_run.c: New test.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
Changes since v1:
- Also handle 8-bit elements (*aarch64_vec_shl_insert_into_zero_vnx16qi).
- Use the attrs column for the instruction type.
- Test the floating-point element types too.  Rename the run test to
  insr-zero-gpr_run.c.

 gcc/config/aarch64/aarch64-sve.md             |  12 +-
 gcc/testsuite/gcc.target/aarch64/sve/copsi.c  |   1 -
 .../gcc.target/aarch64/sve/insr-zero-gpr.c    | 165 ++++++++++++++++++
 .../aarch64/sve/insr-zero-gpr_run.c           |  72 ++++++++
 4 files changed, 243 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/insr-zero-gpr.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/insr-zero-gpr_run.c

diff --git a/gcc/config/aarch64/aarch64-sve.md 
b/gcc/config/aarch64/aarch64-sve.md
index dc92cef4d..735983e4d 100644
--- a/gcc/config/aarch64/aarch64-sve.md
+++ b/gcc/config/aarch64/aarch64-sve.md
@@ -3029,10 +3029,10 @@
           (match_operand:<VEL> 2 "register_operand")]
          UNSPEC_INSR))]
   "TARGET_SVE"
-  {@ [ cons: =0 , 1  , 2 ]
-     [ w        , Dz , w ] fmov\t%<Vetype>0, %<Vetype>2
+  {@ [ cons: =0 , 1  , 2 ; attrs: type ]
+     [ w        , Dz , w ; neon_move    ] fmov\t%<Vetype>0, %<Vetype>2
+     [ w        , Dz , r ; f_mcr        ] fmov\t%<Vetype>0, %<vccore>2
   }
-  [(set_attr "type" "neon_move")]
 )
 
 ;; Shift an SVE vector left and insert a scalar into element 0 of a zero
@@ -3044,10 +3044,10 @@
           (match_operand:QI 2 "register_operand")]
          UNSPEC_INSR))]
   "TARGET_SVE"
-  {@ [ cons: =0 , 1  , 2 ; attrs: length ]
-     [ w        , Dz , w ; 8              ] fmov\t%h0, %h2\;and\t%0.h, %0.h, 
#0xff
+  {@ [ cons: =0 , 1  , 2 ; attrs: length , type      ]
+     [ w        , Dz , w ; 8              , neon_move ] fmov\t%h0, 
%h2\;and\t%0.h, %0.h, #0xff
+     [ w        , Dz , r ; 8              , f_mcr     ] fmov\t%h0, 
%w2\;and\t%0.h, %0.h, #0xff
   }
-  [(set_attr "type" "neon_move")]
 )
 
 ;; Shift an SVE vector left and insert a scalar into element 0 from a memory
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/copsi.c 
b/gcc/testsuite/gcc.target/aarch64/sve/copsi.c
index d85403640..6bcbffe64 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/copsi.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/copsi.c
@@ -20,7 +20,6 @@ func_init4 (int64_t a, int64_t b)
 /*
 ** func_init3:
 **     fmov    s0, w2
-**     fmov    s0, s0
 **     insr    z0\.s, w1
 **     insr    z0\.s, w0
 **     ret
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/insr-zero-gpr.c 
b/gcc/testsuite/gcc.target/aarch64/sve/insr-zero-gpr.c
new file mode 100644
index 000000000..4aaf9bfd4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/insr-zero-gpr.c
@@ -0,0 +1,165 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <arm_sve.h>
+#include <string.h>
+
+/* Insert a scalar from a general register into an otherwise zero vector.
+   The scalar move also clears the rest of the SVE register.  */
+
+/*
+** zero_8:
+**     fmov    h0, w0
+**     and     z0\.h, z0\.h, #0xff
+**     ret
+*/
+__attribute__ ((noipa))
+svuint8_t
+zero_8 (uint8_t value)
+{
+  return svinsr_n_u8 (svdup_n_u8 (0), value);
+}
+
+/*
+** nonzero_8:
+**     insr    z0\.b, w0
+**     ret
+*/
+__attribute__ ((noipa))
+svuint8_t
+nonzero_8 (svuint8_t vector, uint8_t value)
+{
+  return svinsr_n_u8 (vector, value);
+}
+
+/*
+** zero_16:
+**     fmov    h0, w0
+**     ret
+*/
+__attribute__ ((noipa))
+svuint16_t
+zero_16 (uint16_t value)
+{
+  return svinsr_n_u16 (svdup_n_u16 (0), value);
+}
+
+/*
+** nonzero_16:
+**     insr    z0\.h, w0
+**     ret
+*/
+__attribute__ ((noipa))
+svuint16_t
+nonzero_16 (svuint16_t vector, uint16_t value)
+{
+  return svinsr_n_u16 (vector, value);
+}
+
+/*
+** zero_32:
+**     fmov    s0, w0
+**     ret
+*/
+__attribute__ ((noipa))
+svuint32_t
+zero_32 (uint32_t value)
+{
+  return svinsr_n_u32 (svdup_n_u32 (0), value);
+}
+
+/*
+** nonzero_32:
+**     insr    z0\.s, w0
+**     ret
+*/
+__attribute__ ((noipa))
+svuint32_t
+nonzero_32 (svuint32_t vector, uint32_t value)
+{
+  return svinsr_n_u32 (vector, value);
+}
+
+/*
+** zero_64:
+**     fmov    d0, x0
+**     ret
+*/
+__attribute__ ((noipa))
+svuint64_t
+zero_64 (uint64_t value)
+{
+  return svinsr_n_u64 (svdup_n_u64 (0), value);
+}
+
+/*
+** nonzero_64:
+**     insr    z0\.d, x0
+**     ret
+*/
+__attribute__ ((noipa))
+svuint64_t
+nonzero_64 (svuint64_t vector, uint64_t value)
+{
+  return svinsr_n_u64 (vector, value);
+}
+
+/* The same applies to floating-point elements whose bits are in a general
+   register.  */
+
+/*
+** zero_f16:
+**     fmov    h0, w0
+**     ret
+*/
+__attribute__ ((noipa))
+svfloat16_t
+zero_f16 (uint16_t bits)
+{
+  float16_t value;
+  memcpy (&value, &bits, sizeof (value));
+  return svinsr_n_f16 (svdup_n_f16 (0), value);
+}
+
+/*
+** zero_bf16:
+**     fmov    h0, w0
+**     ret
+*/
+__attribute__ ((noipa))
+svbfloat16_t
+zero_bf16 (uint16_t bits)
+{
+  bfloat16_t value;
+  memcpy (&value, &bits, sizeof (value));
+  return svinsr_n_bf16 (svreinterpret_bf16 (svdup_n_u16 (0)), value);
+}
+
+/*
+** zero_f32:
+**     fmov    s0, w0
+**     ret
+*/
+__attribute__ ((noipa))
+svfloat32_t
+zero_f32 (uint32_t bits)
+{
+  float32_t value;
+  memcpy (&value, &bits, sizeof (value));
+  return svinsr_n_f32 (svdup_n_f32 (0), value);
+}
+
+/*
+** zero_f64:
+**     fmov    d0, x0
+**     ret
+*/
+__attribute__ ((noipa))
+svfloat64_t
+zero_f64 (uint64_t bits)
+{
+  float64_t value;
+  memcpy (&value, &bits, sizeof (value));
+  return svinsr_n_f64 (svdup_n_f64 (0), value);
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/insr-zero-gpr_run.c 
b/gcc/testsuite/gcc.target/aarch64/sve/insr-zero-gpr_run.c
new file mode 100644
index 000000000..37d56a2a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/insr-zero-gpr_run.c
@@ -0,0 +1,72 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O2" } */
+
+#include "insr-zero-gpr.c"
+
+#define CHECK(BITS, COUNT) \
+static __attribute__ ((noinline, noclone)) void \
+check_##BITS (uint##BITS##_t value) \
+{ \
+  unsigned long count = COUNT; \
+  uint##BITS##_t input[count], output[count]; \
+  for (unsigned long i = 0; i < count; ++i) \
+    input[i] = (uint##BITS##_t) (i * 13 + 7); \
+  svst1_u##BITS (svptrue_b##BITS (), output, zero_##BITS (value)); \
+  if (output[0] != value) \
+    __builtin_abort (); \
+  for (unsigned long i = 1; i < count; ++i) \
+    if (output[i] != 0) \
+      __builtin_abort (); \
+  svuint##BITS##_t v = svld1_u##BITS (svptrue_b##BITS (), input); \
+  svst1_u##BITS (svptrue_b##BITS (), output, nonzero_##BITS (v, value)); \
+  if (output[0] != value) \
+    __builtin_abort (); \
+  for (unsigned long i = 1; i < count; ++i) \
+    if (output[i] != input[i - 1]) \
+      __builtin_abort (); \
+}
+
+CHECK (8, svcntb ())
+CHECK (16, svcnth ())
+CHECK (32, svcntw ())
+CHECK (64, svcntd ())
+
+#define CHECK_FLOAT(NAME, BITS, COUNT) \
+static __attribute__ ((noinline, noclone)) void \
+check_##NAME (uint##BITS##_t value) \
+{ \
+  unsigned long count = COUNT; \
+  uint##BITS##_t output[count]; \
+  svst1_u##BITS (svptrue_b##BITS (), output, \
+                svreinterpret_u##BITS (zero_##NAME (value))); \
+  if (output[0] != value) \
+    __builtin_abort (); \
+  for (unsigned long i = 1; i < count; ++i) \
+    if (output[i] != 0) \
+      __builtin_abort (); \
+}
+
+CHECK_FLOAT (f16, 16, svcnth ())
+CHECK_FLOAT (bf16, 16, svcnth ())
+CHECK_FLOAT (f32, 32, svcntw ())
+CHECK_FLOAT (f64, 64, svcntd ())
+
+int
+main (void)
+{
+  const uint64_t values[] = { 0, 1, 0x80, 0x8000, 0x80000000ULL,
+                            0x8000000000000000ULL, UINT64_MAX,
+                            0x123456789abcdef0ULL };
+  for (unsigned int i = 0; i < sizeof (values) / sizeof (values[0]); ++i)
+    {
+      check_8 (values[i]);
+      check_16 (values[i]);
+      check_32 (values[i]);
+      check_64 (values[i]);
+      check_f16 (values[i]);
+      check_bf16 (values[i]);
+      check_f32 (values[i]);
+      check_f64 (values[i]);
+    }
+  return 0;
+}
-- 
2.43.0

Reply via email to