aarch64_<sra_op>rshr_n<mode><vczle><vczbe>_insn and
aarch64_<sra_op>rsra_n<mode>_insn model the rounding add and the shift in
<V2XWIDE>, as SRSHR/URSHR and SRSRA/URSRA do, but take the shift amount
as a vector in <MODE>.  Only their own expanders build that, so nothing
but the RTL they emit themselves can ever match them: they are reachable
from the ACLE and from nothing else.  The same operation written in C
reaches combine with the shift amount in the mode of the shift, so it
fails to match and comes out as movi + uaddw + shrn.

Take the shift amount in <V2XWIDE> in both patterns and build it there in
both expanders.  The accepted range is unchanged; it comes from
aarch64_simd_shift_imm_<vec_or_offset>_<Vel>, which is keyed on <MODE>.
The two have to move together, because combine turns an add of a rounding
shift into SRSRA and folds SRSRA against a zero accumulator back into
SRSHR, so converting one alone would cost that fusion.

The widened shape is the only correct one: the source-width spelling
(x + (1 << (n - 1))) >> n is a different operation and is deliberately not
matched, since under qemu vrshrq_n_s32 (INT_MAX, 8) is 8388608 where the
C expression is -8388608.  Only the 64-bit-result forms become reachable
from C; srshr v.16b, v.8h and v.4s stay intrinsic-only because their
<V2XWIDE> modes are V16HI, V8SI and V4DI, which the vectoriser does not
use.  The RSHRN, RSHRN2 and SQRSHRUN recognisers keep the same
construction and stay ACLE-only for the same reason; 1/2 gives the RSHRN
pair source-width companions instead, which the saturating forms cannot
have.

For an eight-byte uint8_t block at -O2, movi + ldr + uaddw + shrn + str
becomes ldr + urshr + str.

Cross-tested aarch64-linux-gnu under qemu-aarch64 (aarch64.exp, simd.exp,
advsimd-intrinsics.exp, vect.exp, like for like against the same tree
unpatched) and bootstrapped on x86_64-pc-linux-gnu.

Assisted-by: Claude Opus 5 (Anthropic)

gcc/ChangeLog:

        * config/aarch64/aarch64-simd.md
        (aarch64_<sra_op>rsra_n<mode>_insn): Take the shift amount in
        <V2XWIDE> mode.
        (aarch64_<sra_op>rsra_n<mode>): Build the shift amount vector in
        that mode.
        (aarch64_<sra_op>rshr_n<mode><vczle><vczbe>_insn): Take the shift
        amount in <V2XWIDE> mode.
        (aarch64_<sra_op>rshr_n<mode>): Build the shift amount vector in
        that mode.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/simd_srshr_1.c: New test.
        * gcc.target/aarch64/simd_srshr_2.c: New test.

Signed-off-by: Dominic P <[email protected]>
---
 gcc/config/aarch64/aarch64-simd.md            | 10 ++-
 .../gcc.target/aarch64/simd_srshr_1.c         | 90 +++++++++++++++++++
 .../gcc.target/aarch64/simd_srshr_2.c         | 62 +++++++++++++
 3 files changed, 158 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/simd_srshr_1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/simd_srshr_2.c

diff --git a/gcc/config/aarch64/aarch64-simd.md 
b/gcc/config/aarch64/aarch64-simd.md
index a07e43d91d3..451ea9ff1a5 100644
--- a/gcc/config/aarch64/aarch64-simd.md
+++ b/gcc/config/aarch64/aarch64-simd.md
@@ -1635,7 +1635,8 @@
                (<SHIFTEXTEND>:<V2XWIDE>
                  (match_operand:VSDQ_I_DI 2 "register_operand" "w"))
                (match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand"))
-             (match_operand:VSDQ_I_DI 3 
"aarch64_simd_shift_imm_<vec_or_offset>_<Vel>")))
+             (match_operand:<V2XWIDE> 3
+               "aarch64_simd_shift_imm_<vec_or_offset>_<Vel>")))
          (match_operand:VSDQ_I_DI 1 "register_operand" "0")))]
   "TARGET_SIMD
    && aarch64_const_vec_rnd_cst_p (operands[4], operands[3])"
@@ -1674,7 +1675,7 @@
     rtx rnd = immed_wide_int_const (rnd_wi, GET_MODE_INNER (<V2XWIDE>mode));
     if (VECTOR_MODE_P (<MODE>mode))
       {
-       shft = gen_const_vec_duplicate (<MODE>mode, shft);
+       shft = gen_const_vec_duplicate (<V2XWIDE>mode, shft);
        rnd = gen_const_vec_duplicate (<V2XWIDE>mode, rnd);
       }
 
@@ -7393,7 +7394,8 @@
              (<SHIFTEXTEND>:<V2XWIDE>
                (match_operand:VSDQ_I_DI 1 "register_operand" "w"))
              (match_operand:<V2XWIDE> 3 "aarch64_int_rnd_operand"))
-           (match_operand:VSDQ_I_DI 2 
"aarch64_simd_shift_imm_<vec_or_offset>_<Vel>"))))]
+           (match_operand:<V2XWIDE> 2
+             "aarch64_simd_shift_imm_<vec_or_offset>_<Vel>"))))]
   "TARGET_SIMD
    && aarch64_const_vec_rnd_cst_p (operands[3], operands[2])"
   "<sra_op>rshr\t%<v>0<Vmtype>, %<v>1<Vmtype>, %2"
@@ -7416,7 +7418,7 @@
     rtx rnd = immed_wide_int_const (rnd_wi, GET_MODE_INNER (<V2XWIDE>mode));
     if (VECTOR_MODE_P (<MODE>mode))
       {
-       shft = gen_const_vec_duplicate (<MODE>mode, shft);
+       shft = gen_const_vec_duplicate (<V2XWIDE>mode, shft);
        rnd = gen_const_vec_duplicate (<V2XWIDE>mode, rnd);
       }
 
diff --git a/gcc/testsuite/gcc.target/aarch64/simd_srshr_1.c 
b/gcc/testsuite/gcc.target/aarch64/simd_srshr_1.c
new file mode 100644
index 00000000000..aec88349ccd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/simd_srshr_1.c
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=armv8-a" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <stdint.h>
+
+typedef int8_t v8qi __attribute__((vector_size (8)));
+typedef int16_t v8hi __attribute__((vector_size (16)));
+typedef uint8_t v8uqi __attribute__((vector_size (8)));
+typedef uint16_t v8uhi __attribute__((vector_size (16)));
+typedef int16_t v4hi __attribute__((vector_size (8)));
+typedef int32_t v4si __attribute__((vector_size (16)));
+typedef uint16_t v4uhi __attribute__((vector_size (8)));
+typedef uint32_t v4usi __attribute__((vector_size (16)));
+
+/*
+** s8:
+**     srshr   v0\.8b, v0\.8b, 3
+**     ret
+*/
+v8qi
+s8 (v8qi x)
+{
+  v8hi w = __builtin_convertvector (x, v8hi);
+  return __builtin_convertvector ((w + 4) >> 3, v8qi);
+}
+
+/*
+** u8:
+**     urshr   v0\.8b, v0\.8b, 3
+**     ret
+*/
+v8uqi
+u8 (v8uqi x)
+{
+  v8uhi w = __builtin_convertvector (x, v8uhi);
+  return __builtin_convertvector ((w + 4) >> 3, v8uqi);
+}
+
+/*
+** s16:
+**     srshr   v0\.4h, v0\.4h, 6
+**     ret
+*/
+v4hi
+s16 (v4hi x)
+{
+  v4si w = __builtin_convertvector (x, v4si);
+  return __builtin_convertvector ((w + 32) >> 6, v4hi);
+}
+
+/*
+** u16:
+**     urshr   v0\.4h, v0\.4h, 6
+**     ret
+*/
+v4uhi
+u16 (v4uhi x)
+{
+  v4usi w = __builtin_convertvector (x, v4usi);
+  return __builtin_convertvector ((w + 32) >> 6, v4uhi);
+}
+
+/*
+** loop_s8:
+**     ldr     d31, \[x1\]
+**     srshr   v31\.8b, v31\.8b, 3
+**     str     d31, \[x0\]
+**     ret
+*/
+void
+loop_s8 (int8_t *__restrict d, const int8_t *__restrict s)
+{
+  for (int i = 0; i < 8; i++)
+    d[i] = (int8_t) ((s[i] + 4) >> 3);
+}
+
+/*
+** loop_u8:
+**     ldr     d31, \[x1\]
+**     urshr   v31\.8b, v31\.8b, 3
+**     str     d31, \[x0\]
+**     ret
+*/
+void
+loop_u8 (uint8_t *__restrict d, const uint8_t *__restrict s)
+{
+  for (int i = 0; i < 8; i++)
+    d[i] = (uint8_t) ((s[i] + 4) >> 3);
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/simd_srshr_2.c 
b/gcc/testsuite/gcc.target/aarch64/simd_srshr_2.c
new file mode 100644
index 00000000000..51129fdd165
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/simd_srshr_2.c
@@ -0,0 +1,62 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -march=armv8-a" } */
+
+#include <stdint.h>
+
+typedef int8_t v8qi __attribute__((vector_size (8)));
+typedef int16_t v8hi __attribute__((vector_size (16)));
+typedef uint8_t v8uqi __attribute__((vector_size (8)));
+typedef uint16_t v8uhi __attribute__((vector_size (16)));
+typedef int16_t v4hi __attribute__((vector_size (8)));
+typedef int32_t v4si __attribute__((vector_size (16)));
+typedef uint16_t v4uhi __attribute__((vector_size (8)));
+typedef uint32_t v4usi __attribute__((vector_size (16)));
+
+#define ROUND(NAME, NARROW, WIDE, ELT, WELT, LANES, N)                 \
+  __attribute__((noipa)) static NARROW                                 \
+  NAME (NARROW x)                                                      \
+  {                                                                    \
+    WIDE w = __builtin_convertvector (x, WIDE);                                
\
+    return __builtin_convertvector ((w + (1 << (N - 1))) >> N, NARROW);        
\
+  }                                                                    \
+  __attribute__((noipa)) static void                                   \
+  NAME##_check (void)                                                  \
+  {                                                                    \
+    NARROW x, r;                                                       \
+    for (int rep = 0; rep < 4096; rep++)                               \
+      {                                                                        
\
+       for (int i = 0; i < LANES; i++)                                 \
+         x[i] = (ELT) rnd ();                                          \
+       r = NAME (x);                                                   \
+       for (int i = 0; i < LANES; i++)                                 \
+         {                                                             \
+           WELT w = (WELT) x[i] + (1 << (N - 1));                      \
+           if (r[i] != (ELT) (w >> N))                                 \
+             __builtin_abort ();                                       \
+         }                                                             \
+      }                                                                        
\
+  }
+
+static uint64_t seed = 0x123456789abcdefULL;
+
+static uint64_t
+rnd (void)
+{
+  seed = seed * 6364136223846793005ULL + 1442695040888963407ULL;
+  return seed >> 11;
+}
+
+ROUND (s8, v8qi, v8hi, int8_t, int16_t, 8, 3)
+ROUND (u8, v8uqi, v8uhi, uint8_t, uint16_t, 8, 3)
+ROUND (s16, v4hi, v4si, int16_t, int32_t, 4, 6)
+ROUND (u16, v4uhi, v4usi, uint16_t, uint32_t, 4, 6)
+
+int
+main (void)
+{
+  s8_check ();
+  u8_check ();
+  s16_check ();
+  u16_check ();
+  return 0;
+}
-- 
2.55.0

Reply via email to