From: Richard Earnshaw <[email protected]>

The Neon instruction set lacks a direct vdup from a lane in a 128-bit
vector; but one isn't needed because the lane is a constant and we can
handle the appropriate half of a 128-bit register simply by selecting
that during output.

Additionally, we can also handle core registers as the source operand
for both 64-bit and 128-bit sources without needing to copy the entire
vector to VFP/SIMD regs; at most a simple shift is needed to extract
the appropriate lane to the lower bits of a scratch core reg, but in
some cases we can use the core reg directly (when no shift is needed)
since the upper bits are ignored.

gcc/ChangeLog:

        * config/arm/neon.md (neon_vdup_lane<mode>_internal): Handle
        core registers as the input vector operand by splitting.
        (neon_vdupq_lane<mode>_internal): New pattern

gcc/testsuite/ChangeLog:

        * gcc.target/arm/crypto-vsha1cq_u32.c: Don't expect a vmov.32 in
        the generated code.
        * gcc.target/arm/crypto-vsha1h_u32.c: Likewise.
        * gcc.target/arm/crypto-vsha1mq_u32.c: Likewise.
        * gcc.target/arm/crypto-vsha1pq_u32.c: Likewise.
---
 gcc/config/arm/neon.md                        | 99 +++++++++++++++----
 .../gcc.target/arm/crypto-vsha1cq_u32.c       |  2 +-
 .../gcc.target/arm/crypto-vsha1h_u32.c        |  1 -
 .../gcc.target/arm/crypto-vsha1mq_u32.c       |  1 -
 .../gcc.target/arm/crypto-vsha1pq_u32.c       |  1 -
 5 files changed, 82 insertions(+), 22 deletions(-)

diff --git a/gcc/config/arm/neon.md b/gcc/config/arm/neon.md
index 603bdc1ab828..131e361aead9 100644
--- a/gcc/config/arm/neon.md
+++ b/gcc/config/arm/neon.md
@@ -3526,26 +3526,89 @@ if (BYTES_BIG_ENDIAN)
    (set_attr "type" "multiple")]
 )
 
-(define_insn "neon_vdup_lane<mode>_internal"
-  [(set (match_operand:VDQW 0 "s_register_operand" "=w")
-       (vec_duplicate:VDQW 
-          (vec_select:<V_elem>
-            (match_operand:<V_double_vector_mode> 1 "s_register_operand" "w")
-            (parallel [(match_operand:SI 2 "immediate_operand" "i")]))))]
+(define_insn_and_split "neon_vdup_lane<mode>_internal"
+  [(set (match_operand:VDQW 0 "s_register_operand" "=w,w")
+       (vec_duplicate:VDQW
+         (vec_select:<V_elem>
+           (match_operand:<V_double_vector_mode> 1 "s_register_operand" "w,r")
+           (parallel [(match_operand:SI 2 "immediate_operand" "i,i")]))))
+   (clobber (match_scratch:<V_elem> 3 "=X,r"))]
   "TARGET_NEON"
-{
-  if (BYTES_BIG_ENDIAN)
-    {
-      int elt = INTVAL (operands[2]);
+  {
+    if (REGNO (operands[1]) <= LAST_ARM_REGNUM)
+      return "#";
+    if (BYTES_BIG_ENDIAN)
+      {
+       int elt = INTVAL (operands[2]);
+       elt = GET_MODE_NUNITS (<V_double_vector_mode>mode) - 1 - elt;
+       operands[2] = GEN_INT (elt);
+      }
+    if (<Is_d_reg>)
+      return "vdup.<V_sz_elem>\t%P0, %P1[%c2]";
+    else
+      return "vdup.<V_sz_elem>\t%q0, %P1[%c2]";
+  }
+  "&& REGNO (operands[1]) <= LAST_ARM_REGNUM"
+  [(set (match_dup 0)
+       (vec_duplicate:VDQW (match_dup 3)))]
+  {
+    unsigned HOST_WIDE_INT elt = UINTVAL (operands[2]);
+    if (BYTES_BIG_ENDIAN)
       elt = GET_MODE_NUNITS (<V_double_vector_mode>mode) - 1 - elt;
-      operands[2] = GEN_INT (elt);
-    }
-  if (<Is_d_reg>)
-    return "vdup.<V_sz_elem>\t%P0, %P1[%c2]";
-  else
-    return "vdup.<V_sz_elem>\t%q0, %P1[%c2]";
-}
-  [(set_attr "type" "neon_dup<q>")]
+    unsigned HOST_WIDE_INT size = GET_MODE_SIZE (<V_elem>mode);
+    int base_regno = REGNO (operands[1]);
+    int regno = (base_regno
+                + subreg_regno_offset (base_regno, <V_double_vector_mode>mode,
+                                       elt * size, SImode));
+    unsigned HOST_WIDE_INT offset = (elt * size) % GET_MODE_SIZE (SImode);
+    if (offset != 0)
+      {
+       gcc_assert (offset < 4);
+       rtx reg = gen_rtx_REG (SImode, regno);
+       rtx shift = gen_rtx_LSHIFTRT (SImode, reg,
+                                     GEN_INT (offset * BITS_PER_UNIT));
+       emit_move_insn (gen_rtx_SUBREG (SImode, operands[3], 0),
+                       shift);
+      }
+    else
+      operands[3] = gen_rtx_REG (<V_elem>mode, regno);
+  }
+  [(set_attr "length" "4,8")
+   (set_attr "type" "neon_dup<q>")]
+)
+
+; There isn't an intrinsic for this, but the compiler can generate it
+; idomatically from other operations.
+(define_insn_and_split "neon_vdupq_lane<mode>_internal"
+  [(set (match_operand:VQ2BF 0 "s_register_operand" "=w,w")
+       (vec_duplicate:VQ2BF
+         (vec_select:<V_elem>
+           (match_operand:VQ2BF 1 "s_register_operand" "w,r")
+           (parallel [(match_operand:SI 2 "immediate_operand" "i,i")]))))
+   (clobber (match_scratch:<V_elem> 3 "=X,r"))]
+  "TARGET_NEON"
+  "#"
+  "" ;; && reload_completed"
+  [(parallel
+    [(set (match_dup 0)
+       (vec_duplicate:VQ2BF
+        (vec_select:<V_elem> (match_dup 1) (parallel [(match_dup 2)]))))
+     (clobber (match_dup 3))])]
+  {
+    HOST_WIDE_INT elt = INTVAL (operands[2]);
+    if (elt >= GET_MODE_NUNITS (<MODE>mode) / 2)
+      {
+       elt -= GET_MODE_NUNITS (<MODE>mode) / 2;
+       operands[1] = simplify_gen_subreg (<V_HALF>mode, operands[1],
+                                          <MODE>mode,
+                                          GET_MODE_SIZE (<V_HALF>mode));
+       operands[2] = GEN_INT (elt);
+      }
+    else
+      operands[1] = gen_lowpart (<V_HALF>mode, operands[1]);
+  }
+  [(set_attr "type" "neon_dup<q>")
+   (set_attr "length" "4,8")]
 )
 
 (define_insn "neon_vdup_lane<mode>_internal"
diff --git a/gcc/testsuite/gcc.target/arm/crypto-vsha1cq_u32.c 
b/gcc/testsuite/gcc.target/arm/crypto-vsha1cq_u32.c
index e2835cf4122f..82eb2e3749a0 100644
--- a/gcc/testsuite/gcc.target/arm/crypto-vsha1cq_u32.c
+++ b/gcc/testsuite/gcc.target/arm/crypto-vsha1cq_u32.c
@@ -32,4 +32,4 @@ TEST_SHA1C_VEC_SELECT (GET_LANE)
 
 /* { dg-final { scan-assembler-times {sha1c.32\tq[0-9]+, q[0-9]+} 5 } } */
 /* { dg-final { scan-assembler-times {vdup.32\tq[0-9]+, 
(?:r[0-9]+|d[0-9]+\[[0-9]+\])} 4 { xfail { arm_thumb2 && arm_hf_eabi } } } } */
-/* { dg-final { scan-assembler-times {vmov.32\tr[0-9]+, d[0-9]+\[[0-9]+\]} 3 } 
} */
+
diff --git a/gcc/testsuite/gcc.target/arm/crypto-vsha1h_u32.c 
b/gcc/testsuite/gcc.target/arm/crypto-vsha1h_u32.c
index c67048ab3633..a4e9a48698c3 100644
--- a/gcc/testsuite/gcc.target/arm/crypto-vsha1h_u32.c
+++ b/gcc/testsuite/gcc.target/arm/crypto-vsha1h_u32.c
@@ -28,4 +28,3 @@ TEST_SHA1H_VEC_SELECT (GET_LANE)
 
 /* { dg-final { scan-assembler-times {sha1h.32\tq[0-9]+, q[0-9]+} 5 } } */
 /* { dg-final { scan-assembler-times {vdup.32\tq[0-9]+, 
(?:r[0-9]+|d[0-9]+\[[0-9]+\])} 4 { xfail { arm_thumb2 && arm_hf_eabi } } } } */
-/* { dg-final { scan-assembler-times {vmov.32\tr[0-9]+, d[0-9]+\[[0-9]+\]} 3 } 
} */
diff --git a/gcc/testsuite/gcc.target/arm/crypto-vsha1mq_u32.c 
b/gcc/testsuite/gcc.target/arm/crypto-vsha1mq_u32.c
index 967b682de27f..d1f30d647e36 100644
--- a/gcc/testsuite/gcc.target/arm/crypto-vsha1mq_u32.c
+++ b/gcc/testsuite/gcc.target/arm/crypto-vsha1mq_u32.c
@@ -32,4 +32,3 @@ TEST_SHA1M_VEC_SELECT (GET_LANE)
 
 /* { dg-final { scan-assembler-times {sha1m.32\tq[0-9]+, q[0-9]+} 5 } } */
 /* { dg-final { scan-assembler-times {vdup.32\tq[0-9]+, 
(?:r[0-9]+|d[0-9]+\[[0-9]+\])} 4 { xfail { arm_thumb2 && arm_hf_eabi } } } } */
-/* { dg-final { scan-assembler-times {vmov.32\tr[0-9]+, d[0-9]+\[[0-9]+\]} 3 } 
} */
diff --git a/gcc/testsuite/gcc.target/arm/crypto-vsha1pq_u32.c 
b/gcc/testsuite/gcc.target/arm/crypto-vsha1pq_u32.c
index 09e763256961..43425f3d5ecf 100644
--- a/gcc/testsuite/gcc.target/arm/crypto-vsha1pq_u32.c
+++ b/gcc/testsuite/gcc.target/arm/crypto-vsha1pq_u32.c
@@ -32,4 +32,3 @@ TEST_SHA1P_VEC_SELECT (GET_LANE)
 
 /* { dg-final { scan-assembler-times {sha1p.32\tq[0-9]+, q[0-9]+} 5 } } */
 /* { dg-final { scan-assembler-times {vdup.32\tq[0-9]+, 
(?:r[0-9]+|d[0-9]+\[[0-9]+\])} 4 { xfail { arm_thumb2 && arm_hf_eabi } } } } */
-/* { dg-final { scan-assembler-times {vmov.32\tr[0-9]+, d[0-9]+\[[0-9]+\]} 3 } 
} */
-- 
2.54.0

Reply via email to