[PATCH v2 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.hkwtqyput0.gcc.gcc.rearnsha.208.2.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.

I've also disambiguated the two paterns named
neon_vdup_lane<mode>_internal by inserting the iterator name into the
pattern.  This doesn't change anything in terms of generated code, but
makes the pattern names in the MD file unique.

gcc/ChangeLog:

	* config/arm/neon.md (neon_vdup_lane<VDQW:mode>_internal):
	Handle core registers as the input vector operand by splitting.
	(neon_vdup_lane<VHFBF:mode>_internal): Likewise.
	(neon_vdupq_lane<VQ2BF: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                        | 164 ++++++++++++++----
 .../gcc.target/arm/crypto-vsha1cq_u32.c       |   1 -
 .../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, 127 insertions(+), 41 deletions(-)

diff --git a/gcc/config/arm/neon.md b/gcc/config/arm/neon.md
index 603bdc1ab828..4b5f023162b0 100644
--- a/gcc/config/arm/neon.md
+++ b/gcc/config/arm/neon.md
@@ -3526,48 +3526,138 @@ 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<VDQW: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>")]
 )
 
-(define_insn "neon_vdup_lane<mode>_internal"
- [(set (match_operand:VHFBF 0 "s_register_operand" "=w")
-   (vec_duplicate:VHFBF
-    (vec_select:<V_elem>
-     (match_operand:<V_double_vector_mode> 1 "s_register_operand" "w")
-     (parallel [(match_operand:SI 2 "immediate_operand" "i")]))))]
- "TARGET_NEON && (TARGET_FP16 || TARGET_BF16_SIMD)"
-{
-  if (BYTES_BIG_ENDIAN)
-    {
-      int elt = INTVAL (operands[2]);
+; There isn't an intrinsic for this, but the compiler can generate it
+; idomatically from other operations.
+(define_insn_and_split "neon_vdupq_lane<VQ2BF: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"
+  "#"
+  ""
+  [(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_and_split "neon_vdup_lane<VHFBF:mode>_internal"
+  [(set (match_operand:VHFBF 0 "s_register_operand" "=w,w")
+    (vec_duplicate:VHFBF
+     (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 && (TARGET_FP16 || TARGET_BF16_SIMD)"
+  {
+    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:VHFBF (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>")]
 )
 
 (define_expand "neon_vdup_lane<mode>"
diff --git a/gcc/testsuite/gcc.target/arm/crypto-vsha1cq_u32.c b/gcc/testsuite/gcc.target/arm/crypto-vsha1cq_u32.c
index e2835cf4122f..61c2ee9468c0 100644
--- a/gcc/testsuite/gcc.target/arm/crypto-vsha1cq_u32.c
+++ b/gcc/testsuite/gcc.target/arm/crypto-vsha1cq_u32.c
@@ -32,4 +32,3 @@ 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.