A riscv_rvv_vector_bits type occupies a prefix of the register holding the
RVV type it was derived from, and converts to that type alone: converting
to it drops the trailing lanes, converting back leaves them undefined.
Without this the type cannot be passed to the VLA intrinsics.

Implement TARGET_VECTOR_TYPES_CONVERTIBLE_P and TARGET_CONVERT_TO_TYPE.
The relaxed type drops the mode of the RVV type, so record its index in an
internal "RVV fixed-length type" attribute.  TARGET_COMP_TYPE_ATTRIBUTES
compares it, and riscv_mangle_type uses it to mangle the type as the RVV
type it was derived from, the rule the sizeless types use.

Typedefs with identical arguments have to share one type, or overloading on
them in C++ emits the same symbol twice.  Hold the types built so far in a
GTY root; a hash map does not survive a PCH, whose consumer starts empty
but inherits the types the header was compiled into and would build a
second type with a different TYPE_CANONICAL.

Widening goes through an internal builtin, expanded as a subreg, or via a
stack slot when the subreg would span a VLEN-dependent number of registers.

gcc/
        * config/riscv/riscv-protos.h (lookup_vector_type_index): New.
        (widen_vls_builtin): New.
        (widen_vls_builtin_decl): New.
        (widen_vls_subcode_p): New.
        (vector_type_from_index): New.
        (RISCV_WIDEN_VLS_SUBCODE_BASE): New.
        * config/riscv/riscv-vector-builtins.cc (lookup_vector_type_index):
        New.
        (vector_type_from_index): New.
        (widen_vls_builtin_decls): New.
        (widen_vls_subcode_p): New.
        (widen_vls_builtin_decl): New.
        (widen_vls_builtin): New.
        (register_widen_vls_builtins): New.
        (init_builtins): Call it.
        (handle_pragma_vector): Likewise.
        * config/riscv/riscv-builtins.cc: Include explow.h and emit-rtl.h.
        Assert that riscv_builtins stays below the widening subcode base.
        (riscv_builtin_decl): Return the widening builtins.
        (riscv_expand_builtin): Expand a widening builtin as a subreg, or
        through a stack slot.
        * config/riscv/riscv-c.cc (riscv_check_builtin_call): Reject a
        user call to a widening builtin.
        * config/riscv/riscv.cc (riscv_gnu_attributes): Add the internal
        "RVV fixed-length type" attribute.
        (rvv_vls_types): New.
        (riscv_vls_base_index): New.
        (riscv_build_vls_type): Reject a non-RVV type.  Record in the
        attribute the RVV type the fixed-length type was derived from,
        reusing a previously built type where there is one.
        (riscv_vls_base_type): New.
        (riscv_vector_types_convertible_p): New.  Implement
        TARGET_VECTOR_TYPES_CONVERTIBLE_P.
        (riscv_comp_type_attributes): New.  Implement
        TARGET_COMP_TYPE_ATTRIBUTES.
        (riscv_convert_to_type): New.  Implement TARGET_CONVERT_TO_TYPE.
        (riscv_mangle_type): Mangle a fixed-length type as the RVV type it
        was derived from.
        * doc/extend.texi (RISC-V Attributes): Document the conversion and
        the responsibility for vl it leaves to the programmer.

gcc/testsuite/
        * g++.target/riscv/rvv/base/riscv_rvv_vector_bits-1.C: New test.
        * g++.target/riscv/rvv/base/riscv_rvv_vector_bits-2.C: New test.
        * g++.target/riscv/rvv/base/riscv_rvv_vector_bits-3.C: New test.
        * g++.target/riscv/rvv/pch/pch.exp: New.
        * g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.C: New test.
        * g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.Hs: New.
        * gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-23.c: New test.
        * gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-24.c: New test.
        * gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-27.c: New test.
        * gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-28.c: New test.
        * gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-31.c: New test.
        * gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-32.c: New test.
        * gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-36.c: New test.
---
 gcc/config/riscv/riscv-builtins.cc            |  24 ++++
 gcc/config/riscv/riscv-c.cc                   |   7 +
 gcc/config/riscv/riscv-protos.h               |   9 ++
 gcc/config/riscv/riscv-vector-builtins.cc     |  96 +++++++++++++
 gcc/config/riscv/riscv.cc                     | 135 ++++++++++++++++++
 gcc/doc/extend.texi                           |   6 +
 .../riscv/rvv/base/riscv_rvv_vector_bits-1.C  |  44 ++++++
 .../riscv/rvv/base/riscv_rvv_vector_bits-2.C  |  18 +++
 .../riscv/rvv/base/riscv_rvv_vector_bits-3.C  |  24 ++++
 .../g++.target/riscv/rvv/pch/pch.exp          |  45 ++++++
 .../riscv/rvv/pch/riscv_rvv_vector_bits-1.C   |  11 ++
 .../riscv/rvv/pch/riscv_rvv_vector_bits-1.Hs  |   5 +
 .../riscv/rvv/base/riscv_rvv_vector_bits-23.c |  57 ++++++++
 .../riscv/rvv/base/riscv_rvv_vector_bits-24.c |  25 ++++
 .../riscv/rvv/base/riscv_rvv_vector_bits-27.c |  21 +++
 .../riscv/rvv/base/riscv_rvv_vector_bits-28.c |  22 +++
 .../riscv/rvv/base/riscv_rvv_vector_bits-31.c |  43 ++++++
 .../riscv/rvv/base/riscv_rvv_vector_bits-32.c |  11 ++
 .../riscv/rvv/base/riscv_rvv_vector_bits-36.c |   9 ++
 19 files changed, 612 insertions(+)
 create mode 100644 
gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-1.C
 create mode 100644 
gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-2.C
 create mode 100644 
gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-3.C
 create mode 100644 gcc/testsuite/g++.target/riscv/rvv/pch/pch.exp
 create mode 100644 
gcc/testsuite/g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.C
 create mode 100644 
gcc/testsuite/g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.Hs
 create mode 100644 
gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-23.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-24.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-27.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-28.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-31.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-32.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-36.c

diff --git a/gcc/config/riscv/riscv-builtins.cc 
b/gcc/config/riscv/riscv-builtins.cc
index 997e637be91..30ff6452aea 100644
--- a/gcc/config/riscv/riscv-builtins.cc
+++ b/gcc/config/riscv/riscv-builtins.cc
@@ -36,11 +36,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "stringpool.h"
 #include "expr.h"
+#include "explow.h"
 #include "langhooks.h"
 #include "tm_p.h"
 #include "backend.h"
 #include "gimple.h"
 #include "gimple-iterator.h"
+#include "emit-rtl.h"
 
 /* Macros to create an enumeration identifier for a function prototype.  */
 #define RISCV_FTYPE_NAME0(A) RISCV_##A##_FTYPE
@@ -205,6 +207,9 @@ static struct riscv_builtin_description riscv_builtins[] = {
    function isn't defined on this target.  */
 static GTY(()) tree riscv_builtin_decls[ARRAY_SIZE (riscv_builtins)];
 
+/* The widening builtins take subcodes above this table's indices.  */
+STATIC_ASSERT (ARRAY_SIZE (riscv_builtins) <= RISCV_WIDEN_VLS_SUBCODE_BASE);
+
 /* Get the index I of the function declaration for riscv_builtin_decls[I]
    using the instruction code or return null if not defined for the target.  */
 static GTY(()) int riscv_builtin_decl_index[NUM_INSN_CODES];
@@ -309,6 +314,8 @@ riscv_builtin_decl (unsigned int code, bool initialize_p 
ATTRIBUTE_UNUSED)
   switch (code & RISCV_BUILTIN_CLASS)
     {
     case RISCV_BUILTIN_GENERAL:
+      if (riscv_vector::widen_vls_subcode_p (subcode))
+       return riscv_vector::widen_vls_builtin_decl (subcode);
       if (subcode >= ARRAY_SIZE (riscv_builtins))
        return error_mark_node;
       return riscv_builtin_decls[subcode];
@@ -417,6 +424,23 @@ riscv_expand_builtin (tree exp, rtx target, rtx subtarget 
ATTRIBUTE_UNUSED,
       case RISCV_BUILTIN_VECTOR:
        return riscv_vector::expand_builtin (subcode, exp, target);
       case RISCV_BUILTIN_GENERAL: {
+       /* A fixed-length type is the lowpart prefix of the RVV type it widens
+          to, so widening is a subreg that leaves the rest undefined.  */
+       if (riscv_vector::widen_vls_subcode_p (subcode))
+         {
+           gcc_assert (call_expr_nargs (exp) == 1);
+           rtx op = expand_normal (CALL_EXPR_ARG (exp, 0));
+           op = force_reg (GET_MODE (op), op);
+           machine_mode omode = TYPE_MODE (TREE_TYPE (exp));
+           if (rtx res = force_subreg (omode, op, GET_MODE (op), 0))
+             return res;
+
+           /* Cannot widen in a register; go through the stack.  */
+           rtx slot = assign_stack_temp (omode, GET_MODE_SIZE (omode));
+           emit_move_insn (adjust_address (slot, GET_MODE (op), 0), op);
+           return copy_to_reg (slot);
+         }
+
        const struct riscv_builtin_description *d = &riscv_builtins[subcode];
 
        return riscv_expand_builtin_direct (d->icode, target, exp, 
!d->no_target);
diff --git a/gcc/config/riscv/riscv-c.cc b/gcc/config/riscv/riscv-c.cc
index 369aac89c40..319b6ec561e 100644
--- a/gcc/config/riscv/riscv-c.cc
+++ b/gcc/config/riscv/riscv-c.cc
@@ -266,6 +266,13 @@ riscv_check_builtin_call (location_t loc, vec<location_t> 
arg_loc, tree fndecl,
   switch (code & RISCV_BUILTIN_CLASS)
     {
     case RISCV_BUILTIN_GENERAL:
+      /* The widening builtins are built by TARGET_CONVERT_TO_TYPE; reject
+        direct user calls.  */
+      if (riscv_vector::widen_vls_subcode_p (subcode))
+       {
+         error_at (loc, "%qD is for internal use only", fndecl);
+         return false;
+       }
       return true;
 
     case RISCV_BUILTIN_VECTOR:
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index c0fb2839780..81f4a597aed 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -621,6 +621,11 @@ void reinit_builtins (void);
 const char *mangle_builtin_type (const_tree);
 tree lookup_vector_type_attribute (const_tree);
 bool builtin_type_p (const_tree);
+bool lookup_vector_type_index (const_tree, unsigned int *);
+tree widen_vls_builtin (tree);
+tree widen_vls_builtin_decl (unsigned int);
+bool widen_vls_subcode_p (unsigned int);
+tree vector_type_from_index (unsigned int);
 #ifdef GCC_TARGET_H
 bool verify_type_context (location_t, type_context_kind, const_tree, bool);
 bool expand_vec_perm_const (machine_mode, machine_mode, rtx, rtx, rtx,
@@ -844,6 +849,10 @@ enum riscv_builtin_class
 
 const unsigned int RISCV_BUILTIN_SHIFT = 1;
 
+/* Base subcode for internal VLS-to-RVV widening builtins; indexed by
+   RVV type.  */
+const unsigned int RISCV_WIDEN_VLS_SUBCODE_BASE = 0x10000;
+
 /* Mask that selects the riscv_builtin_class part of a function code.  */
 const unsigned int RISCV_BUILTIN_CLASS = (1 << RISCV_BUILTIN_SHIFT) - 1;
 
diff --git a/gcc/config/riscv/riscv-vector-builtins.cc 
b/gcc/config/riscv/riscv-vector-builtins.cc
index a7e5c24281f..53f5560cc8d 100644
--- a/gcc/config/riscv/riscv-vector-builtins.cc
+++ b/gcc/config/riscv/riscv-vector-builtins.cc
@@ -5498,6 +5498,97 @@ builtin_type_p (const_tree type)
   return lookup_vector_type_attribute (type);
 }
 
+/* If TYPE is an ABI-defined RVV type, store its index in *INDEX and return
+   true.  */
+bool
+lookup_vector_type_index (const_tree type, unsigned int *index)
+{
+  if (!type || type == error_mark_node)
+    return false;
+
+  tree main_variant = TYPE_MAIN_VARIANT (const_cast<tree> (type));
+  for (unsigned int i = 0; i < NUM_VECTOR_TYPES; ++i)
+    if (abi_vector_types[i] == main_variant)
+      {
+       *index = i;
+       return true;
+      }
+
+  return false;
+}
+
+/* Return the ABI-defined RVV type with index INDEX.  */
+tree
+vector_type_from_index (unsigned int index)
+{
+  gcc_assert (index < NUM_VECTOR_TYPES);
+  return abi_vector_types[index];
+}
+
+/* One internal builtin per RVV data type, variadic because the fixed-length
+   type it takes is only created later, by riscv_rvv_vector_bits.  */
+static GTY (()) tree widen_vls_builtin_decls[NUM_VECTOR_TYPES];
+
+/* Return true if SUBCODE belongs to one of those builtins.  */
+bool
+widen_vls_subcode_p (unsigned int subcode)
+{
+  return (subcode >= RISCV_WIDEN_VLS_SUBCODE_BASE
+         && subcode < RISCV_WIDEN_VLS_SUBCODE_BASE + NUM_VECTOR_TYPES);
+}
+
+/* Return the builtin with the given SUBCODE.  */
+tree
+widen_vls_builtin_decl (unsigned int subcode)
+{
+  if (!widen_vls_subcode_p (subcode))
+    return error_mark_node;
+
+  unsigned int index = subcode - RISCV_WIDEN_VLS_SUBCODE_BASE;
+  return widen_vls_builtin_decls[index]
+          ? widen_vls_builtin_decls[index] : error_mark_node;
+}
+
+/* Return the builtin that widens a fixed-length type to the RVV type BASE
+   it is a prefix of, or NULL_TREE if BASE has none.  */
+tree
+widen_vls_builtin (tree base)
+{
+  unsigned int index;
+  if (!lookup_vector_type_index (base, &index))
+    return NULL_TREE;
+
+  return widen_vls_builtin_decls[index];
+}
+
+/* Register a widening builtin for every RVV data type.  */
+static void
+register_widen_vls_builtins (void)
+{
+  for (unsigned int i = 0; i < NUM_VECTOR_TYPES; ++i)
+    {
+      tree type = abi_vector_types[i];
+      if (widen_vls_builtin_decls[i] || !type || !VECTOR_TYPE_P (type)
+         || VECTOR_BOOLEAN_TYPE_P (type)
+         || !VECTOR_MODE_P (TYPE_MODE (type)))
+       continue;
+
+      char name[64];
+      int len = snprintf (name, sizeof (name),
+                         "__builtin_riscv_widen_vls_%s",
+                         IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
+      gcc_checking_assert (len >= 0 && (size_t) len < sizeof (name));
+      widen_vls_builtin_decls[i]
+       = add_builtin_function (name,
+                               build_varargs_function_type_list (type,
+                                                                 NULL_TREE),
+                               ((RISCV_WIDEN_VLS_SUBCODE_BASE + i)
+                                  << RISCV_BUILTIN_SHIFT)
+                                 + RISCV_BUILTIN_GENERAL,
+                               BUILT_IN_MD, NULL, NULL);
+    }
+}
+
 /* Initialize all compiler built-ins related to RVV that should be
    defined at start-up.  */
 void
@@ -5511,6 +5602,7 @@ init_builtins ()
       if (!TARGET_VECTOR)
        return;
       register_builtin_types ();
+      register_widen_vls_builtins ();
     }
 }
 
@@ -5678,6 +5770,10 @@ handle_pragma_vector ()
   function_builder builder;
   for (unsigned int i = 0; i < ARRAY_SIZE (function_groups); ++i)
     builder.register_function_group (function_groups[i]);
+
+  /* Pick up the types init_builtins left unregistered; under LTO this is
+     the only call.  */
+  register_widen_vls_builtins ();
 }
 
 /* Find the registered_function with the given subcode (code without
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 2a8900171b4..43effc04ef8 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -1113,6 +1113,8 @@ static const attribute_spec riscv_gnu_attributes[] =
      and are not used externally */
   {"RVV sizeless type", 4, 4, false, true, false, true, NULL, NULL},
   {"RVV type", 0, 0, false, true, false, true, NULL, NULL},
+  /* The RVV type a relaxed fixed-length type was derived from, by index.  */
+  {"RVV fixed-length type", 1, 1, false, true, false, true, NULL, NULL},
   /* This attribute is used to declare a function, forcing it to use the
     standard vector calling convention variant.  Syntax:
     __attribute__((riscv_vector_cc)). */
@@ -8247,6 +8249,28 @@ riscv_handle_type_attribute (tree *node 
ATTRIBUTE_UNUSED, tree name, tree args,
   return NULL_TREE;
 }
 
+/* Every relaxed fixed-length type built so far, so identical typedefs share
+   one type.  A GTY root, not a hash map, so that it survives a PCH.  */
+static GTY(()) vec<tree, va_gc> *rvv_vls_types;
+
+/* Store in *INDEX the RVV type TYPE was derived from by the relaxed form of
+   riscv_rvv_vector_bits, and return whether it was.  */
+
+static bool
+riscv_vls_base_index (const_tree type, unsigned int *index)
+{
+  if (!type || type == error_mark_node || !VECTOR_TYPE_P (type))
+    return false;
+
+  tree attr = lookup_attribute ("RVV fixed-length type",
+                               TYPE_ATTRIBUTES (type));
+  if (!attr)
+    return false;
+
+  *index = tree_to_uhwi (TREE_VALUE (TREE_VALUE (attr)));
+  return true;
+}
+
 /* Build a VLS vector type of ARGS_IN_BITS from RVV TYPE and store in *NODE.  
*/
 
 static tree
@@ -8282,6 +8306,27 @@ riscv_build_vls_type (tree *node, tree type,
       return NULL_TREE;
     }
 
+  unsigned int index;
+  if (!riscv_vector::lookup_vector_type_index (type, &index))
+    {
+      error ("%qs applied to non-RVV type %qT", "riscv_rvv_vector_bits", type);
+      return NULL_TREE;
+    }
+
+  unsigned int i;
+  tree cached;
+  FOR_EACH_VEC_SAFE_ELT (rvv_vls_types, i, cached)
+    {
+      unsigned int cached_index;
+      if (riscv_vls_base_index (cached, &cached_index)
+         && cached_index == index
+         && tree_to_uhwi (TYPE_SIZE (cached)) == args_in_bits)
+       {
+         *node = cached;
+         return NULL_TREE;
+       }
+    }
+
   tree new_type = build_vector_type (TREE_TYPE (type), args_in_bits / 
elt_bits);
 
   /* A size accepted above should have a VLS mode; diagnose it if not.  */
@@ -8292,11 +8337,86 @@ riscv_build_vls_type (tree *node, tree type,
       return NULL_TREE;
     }
 
+  /* NEW_TYPE's mode no longer carries TYPE's LMUL, so record where it came
+     from.  */
+  new_type = build_distinct_type_copy (new_type);
+  TYPE_ATTRIBUTES (new_type)
+    = tree_cons (get_identifier ("RVV fixed-length type"),
+                build_tree_list (NULL_TREE,
+                                 build_int_cst (integer_type_node, index)),
+                TYPE_ATTRIBUTES (new_type));
+
+  vec_safe_push (rvv_vls_types, new_type);
   *node = new_type;
 
   return NULL_TREE;
 }
 
+/* Return the RVV type TYPE was derived from by the relaxed form of
+   riscv_rvv_vector_bits, or NULL_TREE if it was not.  */
+
+static tree
+riscv_vls_base_type (const_tree type)
+{
+  unsigned int index;
+  if (!riscv_vls_base_index (type, &index))
+    return NULL_TREE;
+
+  return riscv_vector::vector_type_from_index (index);
+}
+
+/* Implement TARGET_VECTOR_TYPES_CONVERTIBLE_P: a relaxed fixed-length type
+   interconverts with the RVV type it is a prefix of.  */
+
+static bool
+riscv_vector_types_convertible_p (const_tree type1, const_tree type2)
+{
+  tree base1 = riscv_vls_base_type (type1);
+  tree base2 = riscv_vls_base_type (type2);
+
+  if (base1 && !base2)
+    return base1 == TYPE_MAIN_VARIANT (type2);
+  if (base2 && !base1)
+    return base2 == TYPE_MAIN_VARIANT (type1);
+
+  return false;
+}
+
+/* Implement TARGET_COMP_TYPE_ATTRIBUTES: two relaxed fixed-length types match
+   only if they came from the same RVV type; any other pair matches.  */
+
+static int
+riscv_comp_type_attributes (const_tree type1, const_tree type2)
+{
+  return (riscv_vls_base_type (type1)
+         == riscv_vls_base_type (type2));
+}
+
+/* Implement TARGET_CONVERT_TO_TYPE: widen with undefined upper lanes,
+   narrow by truncating.  */
+
+static tree
+riscv_convert_to_type (tree type, tree expr)
+{
+  tree from_type = TREE_TYPE (expr);
+
+  if (!VECTOR_TYPE_P (type) || !VECTOR_TYPE_P (from_type)
+      || !riscv_vector_types_convertible_p (type, from_type))
+    return NULL_TREE;
+
+  if (tree base = riscv_vls_base_type (from_type))
+    {
+      /* Widening has no tree code; go through an internal builtin.  Every
+        type that can carry the attribute has one.  */
+      tree fn = riscv_vector::widen_vls_builtin (base);
+      gcc_assert (fn);
+      return build_call_expr (fn, 1, expr);
+    }
+
+  return build3 (BIT_FIELD_REF, type, expr, TYPE_SIZE (type),
+                bitsize_zero_node);
+}
+
 static tree
 riscv_handle_rvv_vls_cc_attribute (tree *, tree name, tree args,
                                   ATTRIBUTE_UNUSED int flags,
@@ -13523,6 +13643,12 @@ riscv_mangle_type (const_tree type)
       gcc_unreachable ();
     }
 
+  /* A relaxed fixed-length type mangles as the RVV type it was derived
+     from, following the rule the sizeless types use.  */
+  if (tree base = riscv_vls_base_type (type))
+    if (const char *base_name = riscv_vector::mangle_builtin_type (base))
+      return base_name;
+
   /* Mangle all vector type for vector extension.  */
   /* The mangle name follows the rule of RVV LLVM
      that is "u" + length of (abi_name) + abi_name. */
@@ -17294,6 +17420,15 @@ riscv_memtag_tag_bitsize ()
 #undef TARGET_VERIFY_TYPE_CONTEXT
 #define TARGET_VERIFY_TYPE_CONTEXT riscv_verify_type_context
 
+#undef TARGET_VECTOR_TYPES_CONVERTIBLE_P
+#define TARGET_VECTOR_TYPES_CONVERTIBLE_P riscv_vector_types_convertible_p
+
+#undef TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES riscv_comp_type_attributes
+
+#undef TARGET_CONVERT_TO_TYPE
+#define TARGET_CONVERT_TO_TYPE riscv_convert_to_type
+
 #undef TARGET_ESTIMATED_POLY_VALUE
 #define TARGET_ESTIMATED_POLY_VALUE riscv_estimated_poly_value
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8a5ed0202aa..a323d1c0135 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8422,6 +8422,12 @@ value it was compiled for.
 typedef vbool32_t fixed_b32 __attribute__((riscv_rvv_vector_bits(4)));
 @end smallexample
 
+Such a data type holds the leading lanes of the RVV type it was written
+against, and converts to and from that type alone: converting to it drops
+the trailing lanes, and converting back leaves them undefined.  Keeping
+@code{vl} and any @code{vrgather.vv} index within the lanes the type holds
+is the programmer's responsibility; neither is checked.
+
 The type keeps the LMUL it was written against whatever @var{n} is, so a
 short one does not use fewer registers.  A data type that does not fill
 the register is experimental: its ABI is not specified by the RISC-V psABI
diff --git a/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-1.C 
b/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-1.C
new file mode 100644
index 00000000000..a8ffe3ab93f
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-1.C
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t m1_128 __attribute__((riscv_rvv_vector_bits(128)));
+typedef vint32m1_t m1_64 __attribute__((riscv_rvv_vector_bits(64)));
+typedef vint32mf2_t mf2_64 __attribute__((riscv_rvv_vector_bits(64)));
+
+vint32m1_t
+widen_full (m1_128 a)
+{
+  return a;
+}
+
+m1_128
+narrow_full (vint32m1_t a)
+{
+  return a;
+}
+
+vint32m1_t
+widen_short (m1_64 a)
+{
+  return a;
+}
+
+m1_64
+call_intrinsic (m1_64 a, m1_64 b)
+{
+  return __riscv_vadd_vv_i32m1 (a, b, sizeof (m1_64) / sizeof (int));
+}
+
+vint32m2_t
+other_lmul (m1_128 a)
+{
+  return a; /* { dg-error "cannot convert" } */
+}
+
+vint32m1_t
+other_fraction (mf2_64 a)
+{
+  return a; /* { dg-error "cannot convert" } */
+}
diff --git a/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-2.C 
b/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-2.C
new file mode 100644
index 00000000000..3e4a5c8e99f
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-2.C
@@ -0,0 +1,18 @@
+/* A fixed-length type mangles as the RVV type it was derived from, so
+   distinct LMULs keep distinct names even at equal lane counts.  */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -mrvv-max-lmul=m2 -O3" } */
+
+#include "riscv_vector.h"
+
+typedef vint32mf2_t mf2_64 __attribute__((riscv_rvv_vector_bits(64)));
+typedef vint32m1_t m1_64 __attribute__((riscv_rvv_vector_bits(64)));
+typedef vint32m2_t m2_64 __attribute__((riscv_rvv_vector_bits(64)));
+
+void f (mf2_64) {}
+void f (m1_64) {}
+void f (m2_64) {}
+
+/* { dg-final { scan-assembler "_Z1fu16__rvv_int32mf2_t:" } } */
+/* { dg-final { scan-assembler "_Z1fu15__rvv_int32m1_t:" } } */
+/* { dg-final { scan-assembler "_Z1fu15__rvv_int32m2_t:" } } */
diff --git a/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-3.C 
b/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-3.C
new file mode 100644
index 00000000000..379eed8b5a2
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/rvv/base/riscv_rvv_vector_bits-3.C
@@ -0,0 +1,24 @@
+/* Converting a constant of such a type runs the conversion through the C++
+   constant folders.  */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t m1_128 __attribute__((riscv_rvv_vector_bits(128)));
+typedef vint32m1_t m1_64 __attribute__((riscv_rvv_vector_bits(64)));
+
+static const m1_128 full = { 1, 2, 3, 4 };
+static const m1_64 shorter = { 1, 2 };
+
+vint32m1_t
+widen_full_constant ()
+{
+  return full;
+}
+
+vint32m1_t
+widen_shorter_constant ()
+{
+  return shorter;
+}
diff --git a/gcc/testsuite/g++.target/riscv/rvv/pch/pch.exp 
b/gcc/testsuite/g++.target/riscv/rvv/pch/pch.exp
new file mode 100644
index 00000000000..45f7c00b701
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/rvv/pch/pch.exp
@@ -0,0 +1,45 @@
+#   Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite for precompiled header interaction with RVV types,
+# that uses the `dg-pch.exp' driver.
+
+# Exit immediately if this isn't a RISC-V target.
+if ![istarget riscv*-*-*] then {
+  return
+}
+
+# Load support procs.
+load_lib g++-dg.exp
+load_lib dg-pch.exp
+
+# Initialize `dg'.
+dg-init
+pch-init
+
+set old_dg_do_what_default "${dg-do-what-default}"
+
+# Main loop.  The tests carry their own -march/-mabi in dg-options, so that
+# the header and the file including it are compiled the same way.
+foreach test [lsort [glob -nocomplain $srcdir/$subdir/*.C]] {
+    dg-flags-pch $subdir $test "-fno-modules" [list "-O2"] ".H"
+}
+
+set dg-do-what-default "$old_dg_do_what_default"
+
+# All done.
+pch-finish
+dg-finish
diff --git a/gcc/testsuite/g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.C 
b/gcc/testsuite/g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.C
new file mode 100644
index 00000000000..e08c33c76c9
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.C
@@ -0,0 +1,11 @@
+/* A relaxed typedef read back from a PCH must be the same type as an
+   identical one written here, just as it is without a PCH.  */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "riscv_rvv_vector_bits-1.H"
+
+typedef vint32m1_t m1_64_again __attribute__((riscv_rvv_vector_bits(64)));
+
+void f (m1_64) {}
+void f (m1_64_again) {} /* { dg-error "redefinition" } */
diff --git a/gcc/testsuite/g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.Hs 
b/gcc/testsuite/g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.Hs
new file mode 100644
index 00000000000..994b9b9fe0a
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/rvv/pch/riscv_rvv_vector_bits-1.Hs
@@ -0,0 +1,5 @@
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t m1_64 __attribute__((riscv_rvv_vector_bits(64)));
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-23.c 
b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-23.c
new file mode 100644
index 00000000000..75fb710fb43
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-23.c
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t m1_128 __attribute__((riscv_rvv_vector_bits(128)));
+typedef vint32m1_t m1_64 __attribute__((riscv_rvv_vector_bits(64)));
+typedef vint32mf2_t mf2_64 __attribute__((riscv_rvv_vector_bits(64)));
+typedef vfloat32m1_t f32m1_64 __attribute__((riscv_rvv_vector_bits(64)));
+
+vint32m1_t
+widen_full (m1_128 a)
+{
+  return a;
+}
+
+m1_128
+narrow_full (vint32m1_t a)
+{
+  return a;
+}
+
+vint32m1_t
+widen_short (m1_64 a)
+{
+  return a;
+}
+
+m1_64
+narrow_short (vint32m1_t a)
+{
+  return a;
+}
+
+vint32mf2_t
+widen_mf2 (mf2_64 a)
+{
+  return a;
+}
+
+vfloat32m1_t
+widen_float (f32m1_64 a)
+{
+  return a;
+}
+
+m1_64
+call_intrinsic (m1_64 a, m1_64 b)
+{
+  return __riscv_vadd_vv_i32m1 (a, b, sizeof (m1_64) / sizeof (int));
+}
+
+void
+assign (m1_64 *p, vint32m1_t v)
+{
+  *p = v;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-24.c 
b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-24.c
new file mode 100644
index 00000000000..00d25615ecb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-24.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -mrvv-max-lmul=m2 -O3" } */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t m1_128 __attribute__((riscv_rvv_vector_bits(128)));
+typedef vint32mf2_t mf2_64 __attribute__((riscv_rvv_vector_bits(64)));
+
+vint32m2_t
+other_lmul (m1_128 a)
+{
+  return a; /* { dg-error "incompatible types when returning type" } */
+}
+
+vint32m1_t
+other_fraction (mf2_64 a)
+{
+  return a; /* { dg-error "incompatible types when returning type" } */
+}
+
+vint16m1_t
+other_element (m1_128 a)
+{
+  return a; /* { dg-error "incompatible types when returning type" } */
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-27.c 
b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-27.c
new file mode 100644
index 00000000000..a7527bff57d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-27.c
@@ -0,0 +1,21 @@
+/* Widening a shorter type to the RVV type it is a prefix of is a subreg of
+   the wider register, not a copy through memory.  */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t m1_64 __attribute__((riscv_rvv_vector_bits(64)));
+
+void
+add (m1_64 *p, m1_64 *q)
+{
+  m1_64 a = *p;
+  vint32m1_t v = a;
+  v = __riscv_vadd_vv_i32m1 (v, v, 2);
+  *q = v;
+}
+
+/* { dg-final { scan-assembler-not {vmv\.v\.i} } } */
+/* { dg-final { scan-assembler-not {vs1r\.v} } } */
+/* { dg-final { scan-assembler-not {vl1re32\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-28.c 
b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-28.c
new file mode 100644
index 00000000000..1db8cbb9928
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-28.c
@@ -0,0 +1,22 @@
+/* The widening builtins must also be registered on the path LTO takes,
+   which only runs at link time.  */
+/* { dg-do link } */
+/* { dg-require-effective-target lto } */
+/* { dg-require-effective-target riscv_v_ok } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -flto -O2" } */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t m1_64 __attribute__((riscv_rvv_vector_bits(64)));
+
+vint32m1_t
+widen (m1_64 a)
+{
+  return a;
+}
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-31.c 
b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-31.c
new file mode 100644
index 00000000000..e9372130375
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-31.c
@@ -0,0 +1,43 @@
+/* A typedef keeps the LMUL it was written against; other LMULs don't convert. 
 */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -mrvv-max-lmul=m2 -O3" } */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t m1_64 __attribute__((riscv_rvv_vector_bits(64)));
+typedef vint32m1_t m1_128 __attribute__((riscv_rvv_vector_bits(128)));
+
+void
+to_sizeless (m1_64 a)
+{
+  vint32m1_t ok = a;
+  vint32mf2_t narrower = a; /* { dg-error "incompatible types when 
initializing" } */
+  vint32m2_t wider = a; /* { dg-error "incompatible types when initializing" } 
*/
+  (void) ok;
+  (void) narrower;
+  (void) wider;
+}
+
+void
+full_size (m1_128 a)
+{
+  vint32m1_t ok = a;
+  vint32m2_t bad = a; /* { dg-error "incompatible types when initializing" } */
+  (void) ok;
+  (void) bad;
+}
+
+void
+intrinsics (m1_64 a, m1_64 b)
+{
+  m1_64 r = __riscv_vadd_vv_i32m1 (a, b, 2);
+  (void) __riscv_vadd_vv_i32mf2 (a, b, 2); /* { dg-error "incompatible type 
for argument" } */
+  (void) r;
+}
+
+void
+intrinsics_mem (const int *p, int *q)
+{
+  m1_64 v = __riscv_vle32_v_i32m1 (p, 2);
+  __riscv_vse32_v_i32m1 (q, v, 2);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-32.c 
b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-32.c
new file mode 100644
index 00000000000..75bdff233b1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-32.c
@@ -0,0 +1,11 @@
+/* The builtins the conversion lowers to are not for user code to call.  */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O3" } */
+
+#include "riscv_vector.h"
+
+void
+f (void)
+{
+  __builtin_riscv_widen_vls___rvv_int32m1_t (); /* { dg-error "is for internal 
use only" } */
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-36.c 
b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-36.c
new file mode 100644
index 00000000000..e6ba934129a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/riscv_rvv_vector_bits-36.c
@@ -0,0 +1,9 @@
+/* The attribute cannot be applied to a type that already has a fixed
+   length.  */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv_zvl128b -mabi=lp64d -mrvv-vector-bits=zvl -O3" 
} */
+
+#include "riscv_vector.h"
+
+typedef vint32m1_t full __attribute__((riscv_rvv_vector_bits(128)));
+typedef full again __attribute__((riscv_rvv_vector_bits(64))); /* { dg-error 
"'riscv_rvv_vector_bits' applied to non-RVV type" } */
-- 
2.54.0

Reply via email to