[PATCH v1 1/2] arm: handle neon vec_dup from select of 128-bit vector

Richard Earnshaw via Sourceware Forge <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <bmm.hkvpdth7hg.gcc.gcc.rearnsha.208.1.1@forge-stage.sourceware.org>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.