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


Reply via email to