This patch adds vec_concat and vec_init expanders to construct a full
vector from two half-width vectors (e.g. [0 1 2 3] + [4 5 6 7] ->
[0 1 2 3 4 5 6 7]) via vslideup.
Key changes:
- Implement @vec_concat and vec_init expanders to combine two halves via
vslideup.
- Extend VLS_HAS_HALF to include Float16 (HF) and BFloat16 (BF) modes.
- Add VLA_HAS_HALF to support VLA modes.
- Unify them under V_HAS_HALF and generalize V_HALF/v_half (also updating
vec_extract).
- Refactor shuffle_even_odd_patterns to use gen_vec_concat.
Regtested on rv64gcv.
gcc/ChangeLog:
* config/riscv/autovec.md (vec_init<mode><v_half>): New expander.
(vec_extract<mode><v_half>): Use <V_HALF>.
* config/riscv/riscv-v.cc (shuffle_even_odd_patterns): Use
gen_vec_concat.
* config/riscv/vector.md (@vec_concat<mode>): New expander.
* config/riscv/vector-iterators.md (VLS_HAS_HALF): Add HF and BF modes.
(VLA_HAS_HALF, V_HAS_HALF): New mode iterators.
(V_HALF, v_half): Rename from VLS_HALF and vls_half.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/rvv/autovec/vec-concat-1.c: New test.
* gcc.target/riscv/rvv/autovec/vec-concat-2.c: Likewise.
Signed-off-by: Zhongyao Chen <[email protected]>
---
gcc/config/riscv/autovec.md | 29 ++-
gcc/config/riscv/riscv-v.cc | 26 +--
gcc/config/riscv/vector-iterators.md | 201 +++++++++++++++++-
gcc/config/riscv/vector.md | 23 ++
.../riscv/rvv/autovec/vec-concat-1.c | 68 ++++++
.../riscv/rvv/autovec/vec-concat-2.c | 16 ++
6 files changed, 343 insertions(+), 20 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/vec-concat-1.c
create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/vec-concat-2.c
diff --git a/gcc/config/riscv/autovec.md b/gcc/config/riscv/autovec.md
index 12538c8f162..90c4766a4c1 100644
--- a/gcc/config/riscv/autovec.md
+++ b/gcc/config/riscv/autovec.md
@@ -452,6 +452,25 @@
}
)
+;; -------------------------------------------------------------------------
+;; ---- [INT,FP] Initialize from two half-width vectors
+;; -------------------------------------------------------------------------
+
+(define_expand "vec_init<mode><v_half>"
+ [(match_operand:V_HAS_HALF 0 "register_operand")
+ (match_operand 1 "")]
+ "TARGET_VECTOR"
+{
+ gcc_assert (GET_CODE (operands[1]) == PARALLEL);
+ gcc_assert (XVECLEN (operands[1], 0) == 2);
+
+ rtx low = force_reg (<V_HALF>mode, XVECEXP (operands[1], 0, 0));
+ rtx high = force_reg (<V_HALF>mode, XVECEXP (operands[1], 0, 1));
+
+ emit_insn (gen_vec_concat (<MODE>mode, operands[0], low, high));
+ DONE;
+})
+
;; Slide an RVV vector left and insert a scalar into element 0.
(define_expand "vec_shl_insert_<mode>"
[(match_operand:V_VLSI 0 "register_operand")
@@ -1523,15 +1542,15 @@
;; For now this helps optimize VLS subregs like (subreg:V2DI (reg:V4DI) 16)
;; that would otherwise need to go via memory.
-(define_expand "vec_extract<mode><vls_half>"
- [(set (match_operand:<VLS_HALF> 0 "nonimmediate_operand")
- (vec_select:<VLS_HALF>
+(define_expand "vec_extract<mode><v_half>"
+ [(set (match_operand:<V_HALF> 0 "nonimmediate_operand")
+ (vec_select:<V_HALF>
(match_operand:VLS_HAS_HALF 1 "register_operand")
(parallel
[(match_operand 2 "immediate_operand")])))]
"TARGET_VECTOR"
{
- int sz = GET_MODE_NUNITS (<VLS_HALF>mode).to_constant ();
+ int sz = GET_MODE_NUNITS (<V_HALF>mode).to_constant ();
int part = INTVAL (operands[2]);
rtx start = GEN_INT (part * sz);
@@ -1547,7 +1566,7 @@
riscv_vector::BINARY_OP, ops);
}
- emit_move_insn (operands[0], gen_lowpart (<VLS_HALF>mode, tmp));
+ emit_move_insn (operands[0], gen_lowpart (<V_HALF>mode, tmp));
DONE;
})
diff --git a/gcc/config/riscv/riscv-v.cc b/gcc/config/riscv/riscv-v.cc
index dd453aa320b..33002a3c088 100644
--- a/gcc/config/riscv/riscv-v.cc
+++ b/gcc/config/riscv/riscv-v.cc
@@ -4056,11 +4056,11 @@ shuffle_even_odd_patterns (struct expand_vec_perm_d *d)
/* When the element width is smaller than the greatest ELEN, we can use two
vnsrl instructions, each extracting the even/odd elements of one source,
- and a vslideup instruction to merge them into one vector.
+ and vec_concat to merge them into one vector.
- Until we have a "widening" vector concat pattern (just like slideup here
- but with the proper modes) we still need the natural-size check for
- LMUL > 1 cases. */
+ We still need the natural-size check for LMUL > 1 cases because
+ vec_concat relies on lowpart-subreg moves, which would force memory
+ spills for multi-register VLS vectors. */
unsigned int max_elen = TARGET_VECTOR_ELEN_64 ? 64 : 32;
if (GET_MODE_BITSIZE (GET_MODE_INNER (vmode)) * 2 <= max_elen
&& known_le (GET_MODE_SIZE (vmode), riscv_regmode_natural_size (vmode)))
@@ -4074,20 +4074,20 @@ shuffle_even_odd_patterns (struct expand_vec_perm_d *d)
machine_mode vmode_half = get_vector_mode (smode, vlen / 2).require ();
unsigned int shift_amt = even ? 0 : elen;
insn_code icode = code_for_pred_narrow_scalar (LSHIFTRT, vmode_elen2x);
- /* TODO these lowpart subreg workarounds should go, this is actually a
- simple concatenation of two "half"-sized vectors. */
- rtx tmp = gen_reg_rtx (vmode);
+ rtx lo = gen_reg_rtx (vmode_half);
+ rtx hi = gen_reg_rtx (vmode_half);
rtx ops_shift1[]
- = {gen_lowpart (vmode_half, d->target),
- gen_lowpart (vmode_elen2x, d->op0), gen_int_mode (shift_amt, Pmode)};
+ = {lo, gen_lowpart (vmode_elen2x, d->op0),
+ gen_int_mode (shift_amt, Pmode)};
rtx ops_shift2[]
- = {gen_lowpart (vmode_half, tmp), gen_lowpart (vmode_elen2x, d->op1),
+ = {hi, gen_lowpart (vmode_elen2x, d->op1),
gen_int_mode (shift_amt, Pmode)};
emit_vlmax_insn (icode, BINARY_OP, ops_shift1);
emit_vlmax_insn (icode, BINARY_OP, ops_shift2);
- rtx ops[] = {d->target, d->target, tmp, gen_int_mode (vlen / 2, Pmode)};
- icode = code_for_pred_slide (UNSPEC_VSLIDEUP, vmode);
- emit_vlmax_insn (icode, SLIDEUP_OP_MERGE, ops);
+
+ machine_mode imode = related_int_vector_mode (vmode).require ();
+ emit_insn (gen_vec_concat (imode, gen_lowpart (imode, d->target),
+ lo, hi));
return true;
}
diff --git a/gcc/config/riscv/vector-iterators.md
b/gcc/config/riscv/vector-iterators.md
index f236e71c67c..c0475b6b72a 100644
--- a/gcc/config/riscv/vector-iterators.md
+++ b/gcc/config/riscv/vector-iterators.md
@@ -6012,6 +6012,14 @@
(V4DI "riscv_vector::vls_mode_valid_p (V4DImode) && TARGET_VECTOR_ELEN_64")
(V8DI "riscv_vector::vls_mode_valid_p (V8DImode) && TARGET_VECTOR_ELEN_64 &&
TARGET_MIN_VLEN >= 64")
(V16DI "riscv_vector::vls_mode_valid_p (V16DImode) && TARGET_VECTOR_ELEN_64
&& TARGET_MIN_VLEN >= 128")
+ (V2HF "riscv_vector::vls_mode_valid_p (V2HFmode) &&
TARGET_VECTOR_ELEN_FP_16")
+ (V4HF "riscv_vector::vls_mode_valid_p (V4HFmode) &&
TARGET_VECTOR_ELEN_FP_16")
+ (V8HF "riscv_vector::vls_mode_valid_p (V8HFmode) &&
TARGET_VECTOR_ELEN_FP_16")
+ (V16HF "riscv_vector::vls_mode_valid_p (V16HFmode) &&
TARGET_VECTOR_ELEN_FP_16")
+ (V2BF "riscv_vector::vls_mode_valid_p (V2BFmode) &&
TARGET_VECTOR_ELEN_BF_16")
+ (V4BF "riscv_vector::vls_mode_valid_p (V4BFmode) &&
TARGET_VECTOR_ELEN_BF_16")
+ (V8BF "riscv_vector::vls_mode_valid_p (V8BFmode) &&
TARGET_VECTOR_ELEN_BF_16")
+ (V16BF "riscv_vector::vls_mode_valid_p (V16BFmode) &&
TARGET_VECTOR_ELEN_BF_16")
(V2SF "riscv_vector::vls_mode_valid_p (V2SFmode) &&
TARGET_VECTOR_ELEN_FP_32")
(V4SF "riscv_vector::vls_mode_valid_p (V4SFmode) &&
TARGET_VECTOR_ELEN_FP_32")
(V8SF "riscv_vector::vls_mode_valid_p (V8SFmode) &&
TARGET_VECTOR_ELEN_FP_32")
@@ -6046,6 +6054,20 @@
(V128DI "riscv_vector::vls_mode_valid_p (V128DImode) &&
TARGET_VECTOR_ELEN_64 && TARGET_MIN_VLEN >= 1024")
(V256DI "riscv_vector::vls_mode_valid_p (V256DImode) &&
TARGET_VECTOR_ELEN_64 && TARGET_MIN_VLEN >= 2048")
(V512DI "riscv_vector::vls_mode_valid_p (V512DImode) &&
TARGET_VECTOR_ELEN_64 && TARGET_MIN_VLEN >= 4096")
+ (V32HF "riscv_vector::vls_mode_valid_p (V32HFmode) &&
TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64")
+ (V64HF "riscv_vector::vls_mode_valid_p (V64HFmode) &&
TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128")
+ (V128HF "riscv_vector::vls_mode_valid_p (V128HFmode) &&
TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 256")
+ (V256HF "riscv_vector::vls_mode_valid_p (V256HFmode) &&
TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 512")
+ (V512HF "riscv_vector::vls_mode_valid_p (V512HFmode) &&
TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 1024")
+ (V1024HF "riscv_vector::vls_mode_valid_p (V1024HFmode) &&
TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 2048")
+ (V2048HF "riscv_vector::vls_mode_valid_p (V2048HFmode) &&
TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 4096")
+ (V32BF "riscv_vector::vls_mode_valid_p (V32BFmode) &&
TARGET_VECTOR_ELEN_BF_16 && TARGET_MIN_VLEN >= 64")
+ (V64BF "riscv_vector::vls_mode_valid_p (V64BFmode) &&
TARGET_VECTOR_ELEN_BF_16 && TARGET_MIN_VLEN >= 128")
+ (V128BF "riscv_vector::vls_mode_valid_p (V128BFmode) &&
TARGET_VECTOR_ELEN_BF_16 && TARGET_MIN_VLEN >= 256")
+ (V256BF "riscv_vector::vls_mode_valid_p (V256BFmode) &&
TARGET_VECTOR_ELEN_BF_16 && TARGET_MIN_VLEN >= 512")
+ (V512BF "riscv_vector::vls_mode_valid_p (V512BFmode) &&
TARGET_VECTOR_ELEN_BF_16 && TARGET_MIN_VLEN >= 1024")
+ (V1024BF "riscv_vector::vls_mode_valid_p (V1024BFmode) &&
TARGET_VECTOR_ELEN_BF_16 && TARGET_MIN_VLEN >= 2048")
+ (V2048BF "riscv_vector::vls_mode_valid_p (V2048BFmode) &&
TARGET_VECTOR_ELEN_BF_16 && TARGET_MIN_VLEN >= 4096")
(V32SF "riscv_vector::vls_mode_valid_p (V32SFmode) &&
TARGET_VECTOR_ELEN_FP_32 && TARGET_MIN_VLEN >= 128")
(V64SF "riscv_vector::vls_mode_valid_p (V64SFmode) &&
TARGET_VECTOR_ELEN_FP_32 && TARGET_MIN_VLEN >= 256")
(V128SF "riscv_vector::vls_mode_valid_p (V128SFmode) &&
TARGET_VECTOR_ELEN_FP_32 && TARGET_MIN_VLEN >= 512")
@@ -6059,7 +6081,98 @@
(V512DF "riscv_vector::vls_mode_valid_p (V512DFmode) &&
TARGET_VECTOR_ELEN_FP_64 && TARGET_MIN_VLEN >= 4096")
])
-(define_mode_attr VLS_HALF [
+;; Scalable (VLA) vector modes with a supported half-width vector.
+(define_mode_iterator VLA_HAS_HALF [
+ RVVM8QI
+ RVVM4QI
+ RVVM2QI
+ (RVVM1QI "!TARGET_XTHEADVECTOR")
+ (RVVMF2QI "!TARGET_XTHEADVECTOR")
+ (RVVMF4QI "!TARGET_XTHEADVECTOR && TARGET_VECTOR_ELEN_64")
+
+ RVVM8HI
+ RVVM4HI
+ RVVM2HI
+ (RVVM1HI "!TARGET_XTHEADVECTOR")
+ (RVVMF2HI "!TARGET_XTHEADVECTOR && TARGET_VECTOR_ELEN_64")
+
+ RVVM8SI
+ RVVM4SI
+ RVVM2SI
+ (RVVM1SI "!TARGET_XTHEADVECTOR && TARGET_VECTOR_ELEN_64")
+
+ (RVVM8DI "TARGET_VECTOR_ELEN_64")
+ (RVVM4DI "TARGET_VECTOR_ELEN_64")
+ (RVVM2DI "TARGET_VECTOR_ELEN_64")
+
+ (RVVM8HF "TARGET_VECTOR_ELEN_FP_16")
+ (RVVM4HF "TARGET_VECTOR_ELEN_FP_16")
+ (RVVM2HF "TARGET_VECTOR_ELEN_FP_16")
+ (RVVM1HF "TARGET_VECTOR_ELEN_FP_16 && !TARGET_XTHEADVECTOR")
+ (RVVMF2HF "TARGET_VECTOR_ELEN_FP_16 && !TARGET_XTHEADVECTOR
+ && TARGET_VECTOR_ELEN_64")
+
+ (RVVM8BF "TARGET_VECTOR_ELEN_BF_16")
+ (RVVM4BF "TARGET_VECTOR_ELEN_BF_16")
+ (RVVM2BF "TARGET_VECTOR_ELEN_BF_16")
+ (RVVM1BF "TARGET_VECTOR_ELEN_BF_16 && !TARGET_XTHEADVECTOR")
+ (RVVMF2BF "TARGET_VECTOR_ELEN_BF_16 && !TARGET_XTHEADVECTOR
+ && TARGET_VECTOR_ELEN_64")
+
+ (RVVM8SF "TARGET_VECTOR_ELEN_FP_32")
+ (RVVM4SF "TARGET_VECTOR_ELEN_FP_32")
+ (RVVM2SF "TARGET_VECTOR_ELEN_FP_32")
+ (RVVM1SF "TARGET_VECTOR_ELEN_FP_32 && !TARGET_XTHEADVECTOR
+ && TARGET_VECTOR_ELEN_64")
+
+ (RVVM8DF "TARGET_VECTOR_ELEN_FP_64")
+ (RVVM4DF "TARGET_VECTOR_ELEN_FP_64")
+ (RVVM2DF "TARGET_VECTOR_ELEN_FP_64")
+])
+
+;; All vector modes (VLS and VLA) with a supported half-width vector.
+(define_mode_iterator V_HAS_HALF [
+ VLS_HAS_HALF
+ VLA_HAS_HALF
+])
+
+(define_mode_attr V_HALF [
+ (RVVM8QI "RVVM4QI")
+ (RVVM4QI "RVVM2QI")
+ (RVVM2QI "RVVM1QI")
+ (RVVM1QI "RVVMF2QI")
+ (RVVMF2QI "RVVMF4QI")
+ (RVVMF4QI "RVVMF8QI")
+ (RVVM8HI "RVVM4HI")
+ (RVVM4HI "RVVM2HI")
+ (RVVM2HI "RVVM1HI")
+ (RVVM1HI "RVVMF2HI")
+ (RVVMF2HI "RVVMF4HI")
+ (RVVM8SI "RVVM4SI")
+ (RVVM4SI "RVVM2SI")
+ (RVVM2SI "RVVM1SI")
+ (RVVM1SI "RVVMF2SI")
+ (RVVM8DI "RVVM4DI")
+ (RVVM4DI "RVVM2DI")
+ (RVVM2DI "RVVM1DI")
+ (RVVM8HF "RVVM4HF")
+ (RVVM4HF "RVVM2HF")
+ (RVVM2HF "RVVM1HF")
+ (RVVM1HF "RVVMF2HF")
+ (RVVMF2HF "RVVMF4HF")
+ (RVVM8BF "RVVM4BF")
+ (RVVM4BF "RVVM2BF")
+ (RVVM2BF "RVVM1BF")
+ (RVVM1BF "RVVMF2BF")
+ (RVVMF2BF "RVVMF4BF")
+ (RVVM8SF "RVVM4SF")
+ (RVVM4SF "RVVM2SF")
+ (RVVM2SF "RVVM1SF")
+ (RVVM1SF "RVVMF2SF")
+ (RVVM8DF "RVVM4DF")
+ (RVVM4DF "RVVM2DF")
+ (RVVM2DF "RVVM1DF")
+
(V2QI "V1QI")
(V4QI "V2QI")
(V8QI "V4QI")
@@ -6106,6 +6219,30 @@
(V256DI "V128DI")
(V512DI "V256DI")
+ (V2HF "V1HF")
+ (V4HF "V2HF")
+ (V8HF "V4HF")
+ (V16HF "V8HF")
+ (V32HF "V16HF")
+ (V64HF "V32HF")
+ (V128HF "V64HF")
+ (V256HF "V128HF")
+ (V512HF "V256HF")
+ (V1024HF "V512HF")
+ (V2048HF "V1024HF")
+
+ (V2BF "V1BF")
+ (V4BF "V2BF")
+ (V8BF "V4BF")
+ (V16BF "V8BF")
+ (V32BF "V16BF")
+ (V64BF "V32BF")
+ (V128BF "V64BF")
+ (V256BF "V128BF")
+ (V512BF "V256BF")
+ (V1024BF "V512BF")
+ (V2048BF "V1024BF")
+
(V2SF "V1SF")
(V4SF "V2SF")
(V8SF "V4SF")
@@ -6128,7 +6265,43 @@
(V512DF "V256DF")
])
-(define_mode_attr vls_half [
+(define_mode_attr v_half [
+ (RVVM8QI "rvvm4qi")
+ (RVVM4QI "rvvm2qi")
+ (RVVM2QI "rvvm1qi")
+ (RVVM1QI "rvvmf2qi")
+ (RVVMF2QI "rvvmf4qi")
+ (RVVMF4QI "rvvmf8qi")
+ (RVVM8HI "rvvm4hi")
+ (RVVM4HI "rvvm2hi")
+ (RVVM2HI "rvvm1hi")
+ (RVVM1HI "rvvmf2hi")
+ (RVVMF2HI "rvvmf4hi")
+ (RVVM8SI "rvvm4si")
+ (RVVM4SI "rvvm2si")
+ (RVVM2SI "rvvm1si")
+ (RVVM1SI "rvvmf2si")
+ (RVVM8DI "rvvm4di")
+ (RVVM4DI "rvvm2di")
+ (RVVM2DI "rvvm1di")
+ (RVVM8HF "rvvm4hf")
+ (RVVM4HF "rvvm2hf")
+ (RVVM2HF "rvvm1hf")
+ (RVVM1HF "rvvmf2hf")
+ (RVVMF2HF "rvvmf4hf")
+ (RVVM8BF "rvvm4bf")
+ (RVVM4BF "rvvm2bf")
+ (RVVM2BF "rvvm1bf")
+ (RVVM1BF "rvvmf2bf")
+ (RVVMF2BF "rvvmf4bf")
+ (RVVM8SF "rvvm4sf")
+ (RVVM4SF "rvvm2sf")
+ (RVVM2SF "rvvm1sf")
+ (RVVM1SF "rvvmf2sf")
+ (RVVM8DF "rvvm4df")
+ (RVVM4DF "rvvm2df")
+ (RVVM2DF "rvvm1df")
+
(V2QI "v1qi")
(V4QI "v2qi")
(V8QI "v4qi")
@@ -6175,6 +6348,30 @@
(V256DI "v128di")
(V512DI "v256di")
+ (V2HF "v1hf")
+ (V4HF "v2hf")
+ (V8HF "v4hf")
+ (V16HF "v8hf")
+ (V32HF "v16hf")
+ (V64HF "v32hf")
+ (V128HF "v64hf")
+ (V256HF "v128hf")
+ (V512HF "v256hf")
+ (V1024HF "v512hf")
+ (V2048HF "v1024hf")
+
+ (V2BF "v1bf")
+ (V4BF "v2bf")
+ (V8BF "v4bf")
+ (V16BF "v8bf")
+ (V32BF "v16bf")
+ (V64BF "v32bf")
+ (V128BF "v64bf")
+ (V256BF "v128bf")
+ (V512BF "v256bf")
+ (V1024BF "v512bf")
+ (V2048BF "v1024bf")
+
(V2SF "v1sf")
(V4SF "v2sf")
(V8SF "v4sf")
diff --git a/gcc/config/riscv/vector.md b/gcc/config/riscv/vector.md
index 37f7b94604d..11d913d4a9c 100644
--- a/gcc/config/riscv/vector.md
+++ b/gcc/config/riscv/vector.md
@@ -8371,6 +8371,29 @@
[(set_attr "type" "vfmovvf")
(set_attr "mode" "<MODE>")])
+;; Concatenate two half-width vectors. The merge supplies the low half
+;; and vslideup inserts the high half.
+(define_expand "@vec_concat<mode>"
+ [(set (match_operand:V_HAS_HALF 0 "register_operand")
+ (vec_concat:V_HAS_HALF
+ (match_operand:<V_HALF> 1 "register_operand")
+ (match_operand:<V_HALF> 2 "register_operand")))]
+ "TARGET_VECTOR"
+{
+ rtx low = gen_reg_rtx (<MODE>mode);
+ rtx high = gen_reg_rtx (<MODE>mode);
+ emit_clobber (low);
+ emit_move_insn (gen_lowpart (<V_HALF>mode, low), operands[1]);
+ emit_clobber (high);
+ emit_move_insn (gen_lowpart (<V_HALF>mode, high), operands[2]);
+ rtx offset = gen_int_mode (GET_MODE_NUNITS (<V_HALF>mode), Pmode);
+ rtx ops[] = {operands[0], low, high, offset};
+ insn_code icode = code_for_pred_slide (UNSPEC_VSLIDEUP, <MODE>mode);
+ riscv_vector::emit_vlmax_insn (icode,
+ riscv_vector::SLIDEUP_OP_MERGE, ops);
+ DONE;
+})
+
;; vslide instructions
(define_insn "@pred_slide<ud><mode>"
[(set (match_operand:V_VLS 0 "register_operand"
"<ud_constraint>")
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vec-concat-1.c
b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vec-concat-1.c
new file mode 100644
index 00000000000..841e32fc7f4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vec-concat-1.c
@@ -0,0 +1,68 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gcv_zvfhmin_zvfbfmin -mabi=lp64d
-mrvv-vector-bits=scalable -mrvv-max-lmul=m8" } */
+
+/* Test VLS vector concatenation via shufflevector across all data types. */
+
+#define INDICES_2(x) (x), (x + 1)
+#define INDICES_4(x) INDICES_2 (x), INDICES_2 (x + 2)
+#define INDICES_8(x) INDICES_4 (x), INDICES_4 (x + 4)
+#define INDICES_16(x) INDICES_8 (x), INDICES_8 (x + 8)
+#define INDICES_32(x) INDICES_16 (x), INDICES_16 (x + 16)
+#define INDICES_64(x) INDICES_32 (x), INDICES_32 (x + 32)
+
+#define TEST_MODES(T) \
+ T (signed char, qi, 2) \
+ T (signed char, qi, 4) \
+ T (signed char, qi, 8) \
+ T (signed char, qi, 16) \
+ T (signed char, qi, 32) \
+ T (signed char, qi, 64) \
+ T (short, hi, 2) \
+ T (short, hi, 4) \
+ T (short, hi, 8) \
+ T (short, hi, 16) \
+ T (short, hi, 32) \
+ T (int, si, 2) \
+ T (int, si, 4) \
+ T (int, si, 8) \
+ T (int, si, 16) \
+ T (long long, di, 2) \
+ T (long long, di, 4) \
+ T (long long, di, 8) \
+ T (float, sf, 2) \
+ T (float, sf, 4) \
+ T (float, sf, 8) \
+ T (float, sf, 16) \
+ T (double, df, 2) \
+ T (double, df, 4) \
+ T (double, df, 8) \
+ T (_Float16, hf, 2) \
+ T (_Float16, hf, 4) \
+ T (_Float16, hf, 8) \
+ T (_Float16, hf, 16) \
+ T (_Float16, hf, 32) \
+ T (__bf16, bf, 2) \
+ T (__bf16, bf, 4) \
+ T (__bf16, bf, 8) \
+ T (__bf16, bf, 16) \
+ T (__bf16, bf, 32)
+
+#define DEF_CONCAT(TYPE, ID, N) \
+ typedef TYPE half_##ID##N \
+ __attribute__ ((vector_size (N * sizeof (TYPE)))); \
+ typedef TYPE full_##ID##N \
+ __attribute__ ((vector_size (2 * N * sizeof (TYPE)))); \
+ __attribute__ ((noipa)) full_##ID##N \
+ concat_##ID##N (half_##ID##N a, half_##ID##N b) \
+ { \
+ return __builtin_shufflevector (a, b, INDICES_##N (0), INDICES_##N (N)); \
+ }
+
+TEST_MODES (DEF_CONCAT)
+
+/* FIXME (missed optimization): Ideally each concat needs only 1 vslideup,
+ but frontend lowering of __builtin_shufflevector currently zero-pads each
+ half-width input first, resulting in 2 padding constructors and 1 concat
+ per function (35 * 3 = 105). */
+/* { dg-final { scan-assembler-times "vslideup" 105 } } */
+/* { dg-final { scan-assembler-not "vslide1down" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vec-concat-2.c
b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vec-concat-2.c
new file mode 100644
index 00000000000..deab622faec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vec-concat-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=rv64gcv -mabi=lp64d -mrvv-vector-bits=scalable
-mrvv-max-lmul=m1 -fno-vect-cost-model -fdump-tree-vect-details" } */
+
+__attribute__ ((noipa)) void
+test_strided_concat (int *restrict dst, const int *restrict src, long stride)
+{
+#pragma GCC unroll 1
+ for (int i = 0; i < 32; ++i)
+ {
+ dst[2 * i] = src[i * stride];
+ dst[2 * i + 1] = src[i * stride + 1];
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" } } */
+/* { dg-final { scan-assembler-times "vslideup" 1 } } */
--
2.27.0