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