This patch implements the standard fixed-length vector calling convention
variant as specified in the RISC-V ELF psABI document. The implementation
introduces ABI_VLEN to serve as the minimal VLEN for fixed-length vectors.

For example, int32x8_t is a 256-bit vector type. If ABI_VLEN is 128, it
will be passed in two vector registers as LMUL 2. If ABI_VLEN is larger
than 256, it will be passed in one vector register as LMUL 1.

This differs from the minimal VLEN (defined by ZVL*B extension) to ensure
ABI stability when the program compiles with different VLEN/ZVL*B settings.

Change since v1:
- Adding check_only parameter for several functions to make sure we
  won't emit warnings during checking function ABI.

Ref: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/418

gcc/ChangeLog:

        * config/riscv/riscv.h (enum riscv_cc): Add RISCV_CC_VLS_V_32,
        RISCV_CC_VLS_V_64, RISCV_CC_VLS_V_128, RISCV_CC_VLS_V_256,
        RISCV_CC_VLS_V_512, RISCV_CC_VLS_V_1024, RISCV_CC_VLS_V_2048,
        RISCV_CC_VLS_V_4096, RISCV_CC_VLS_V_8192, RISCV_CC_VLS_V_16384.
        (CUMULATIVE_ARGS): Add abi_vlen field.
        * config/riscv/riscv.cc (riscv_handle_rvv_vls_cc_attribute): New
        function.
        (riscv_gnu_attributes): Add vls_cc attribute entry.
        (riscv_attributes): Add riscv_vls_cc attribute entry.
        (riscv_flatten_aggregate_field): Add vls_p and abi_vlen parameters
        to handle VLS vector types.
        (riscv_flatten_aggregate_argument): Update call to
        riscv_flatten_aggregate_field.
        (riscv_get_vector_arg): Add vls_p parameter for VLS handling.
        (riscv_vls_cc_p): New function.
        (riscv_get_cc_abi_vlen): New function.
        (riscv_valid_abi_vlen_vls_cc_p): New function.
        (riscv_get_riscv_cc_by_abi_vlen): New function.
        (riscv_get_vls_container_type): New function.
        (riscv_pass_vls_in_vr): New function.
        (riscv_pass_aggregate_in_vr): New function.
        (riscv_get_arg_info): Add VLS calling convention handling.
        (riscv_function_arg_advance): Update for VLS calling convention.
        (riscv_return_in_memory): Add fntype parameter and initialize
        cumulative args properly.
        (riscv_v_abi): Add abi parameter.
        (riscv_get_vls_cc_attr): New function.
        (riscv_vls_cc_function_abi): New function.
        (riscv_fntype_abi): Add VLS calling convention detection.
        (riscv_asm_output_variant_cc): Update for VLS calling convention.
---
 gcc/config/riscv/riscv.cc | 452 +++++++++++++++++++++++++++++++++-----
 gcc/config/riscv/riscv.h  |  13 ++
 2 files changed, 415 insertions(+), 50 deletions(-)

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index d5de76c342e..10030a53c56 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -740,6 +740,7 @@ static tree riscv_handle_fndecl_attribute (tree *, tree, 
tree, int, bool *);
 static tree riscv_handle_type_attribute (tree *, tree, tree, int, bool *);
 static tree riscv_handle_rvv_vector_bits_attribute (tree *, tree, tree, int,
                                                    bool *);
+static tree riscv_handle_rvv_vls_cc_attribute (tree *, tree, tree, int, bool 
*);
 
 /* Defining target-specific uses of __attribute__.  */
 static const attribute_spec riscv_gnu_attributes[] =
@@ -763,6 +764,8 @@ static const attribute_spec riscv_gnu_attributes[] =
     standard vector calling convention variant. Syntax:
     __attribute__((riscv_vector_cc)). */
   {"riscv_vector_cc", 0, 0, false, true, true, true, NULL, NULL},
+  {"riscv_vls_cc", 0, 1, false, true, true, true,
+   riscv_handle_rvv_vls_cc_attribute, NULL},
   /* This attribute is used to declare a new type, to appoint the exactly
      bits size of the type.  For example:
 
@@ -790,6 +793,8 @@ static const attribute_spec riscv_attributes[] =
      standard vector calling convention variant. Syntax:
      [[riscv::vector_cc]]. */
   {"vector_cc", 0, 0, false, true, true, true, NULL, NULL},
+  {"vls_cc", 0, 1, false, true, true, true, riscv_handle_rvv_vls_cc_attribute,
+   NULL},
   /* This attribute is used to declare a new type, to appoint the exactly
      bits size of the type.  For example:
 
@@ -5872,11 +5877,12 @@ typedef struct {
    floating-point registers.  */
 
 static int
-riscv_flatten_aggregate_field (const_tree type,
-                              riscv_aggregate_field fields[2],
+riscv_flatten_aggregate_field (const_tree type, riscv_aggregate_field *fields,
                               int n, HOST_WIDE_INT offset,
-                              bool ignore_zero_width_bit_field_p)
+                              bool ignore_zero_width_bit_field_p,
+                              bool vls_p = false, unsigned abi_vlen = 0)
 {
+  int max_aggregate_field = vls_p ? 8 : 2;
   switch (TREE_CODE (type))
     {
     case RECORD_TYPE:
@@ -5903,9 +5909,9 @@ riscv_flatten_aggregate_field (const_tree type,
            else
              {
                HOST_WIDE_INT pos = offset + int_byte_position (f);
-               n = riscv_flatten_aggregate_field (TREE_TYPE (f),
-                                                  fields, n, pos,
-                                                  
ignore_zero_width_bit_field_p);
+               n = riscv_flatten_aggregate_field (
+                 TREE_TYPE (f), fields, n, pos, ignore_zero_width_bit_field_p,
+                 vls_p, abi_vlen);
              }
            if (n < 0)
              return -1;
@@ -5915,13 +5921,14 @@ riscv_flatten_aggregate_field (const_tree type,
     case ARRAY_TYPE:
       {
        HOST_WIDE_INT n_elts;
-       riscv_aggregate_field subfields[2];
+       riscv_aggregate_field subfields[8];
        tree index = TYPE_DOMAIN (type);
        tree elt_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
-       int n_subfields = riscv_flatten_aggregate_field (TREE_TYPE (type),
-                                                        subfields, 0, offset,
-                                                        
ignore_zero_width_bit_field_p);
-
+       int n_subfields
+         = riscv_flatten_aggregate_field (TREE_TYPE (type), subfields, 0,
+                                          offset,
+                                          ignore_zero_width_bit_field_p, vls_p,
+                                          abi_vlen);
        /* Can't handle incomplete types nor sizes that are not fixed.  */
        if (n_subfields <= 0
            || !COMPLETE_TYPE_P (type)
@@ -5941,7 +5948,7 @@ riscv_flatten_aggregate_field (const_tree type,
        for (HOST_WIDE_INT i = 0; i < n_elts; i++)
          for (int j = 0; j < n_subfields; j++)
            {
-             if (n >= 2)
+             if (n >= max_aggregate_field)
                return -1;
 
              fields[n] = subfields[j];
@@ -5973,18 +5980,36 @@ riscv_flatten_aggregate_field (const_tree type,
       }
 
     default:
-      if (n < 2
-         && ((SCALAR_FLOAT_TYPE_P (type)
-              && GET_MODE_SIZE (TYPE_MODE (type)).to_constant () <= 
UNITS_PER_FP_ARG)
-             || (INTEGRAL_TYPE_P (type)
-                 && GET_MODE_SIZE (TYPE_MODE (type)).to_constant () <= 
UNITS_PER_WORD)))
+      poly_uint64 mode_size = GET_MODE_SIZE (TYPE_MODE (type));
+      if (vls_p)
        {
-         fields[n].type = type;
-         fields[n].offset = offset;
-         return n + 1;
+         gcc_assert (abi_vlen != 0);
+         if (n < max_aggregate_field
+             && (VECTOR_TYPE_P (type) && mode_size.is_constant ()
+                 && (mode_size.to_constant () <= abi_vlen * 8)))
+           {
+             fields[n].type = type;
+             fields[n].offset = offset;
+             return n + 1;
+           }
+         else
+           return -1;
        }
       else
-       return -1;
+       {
+         if (n < max_aggregate_field
+             && ((SCALAR_FLOAT_TYPE_P (type)
+                  && mode_size.to_constant () <= UNITS_PER_FP_ARG)
+                 || (INTEGRAL_TYPE_P (type)
+                     && mode_size.to_constant () <= UNITS_PER_WORD)))
+           {
+             fields[n].type = type;
+             fields[n].offset = offset;
+             return n + 1;
+           }
+         else
+           return -1;
+       }
     }
 }
 
@@ -5993,14 +6018,16 @@ riscv_flatten_aggregate_field (const_tree type,
 
 static int
 riscv_flatten_aggregate_argument (const_tree type,
-                                 riscv_aggregate_field fields[2],
-                                 bool ignore_zero_width_bit_field_p)
+                                 riscv_aggregate_field *fields,
+                                 bool ignore_zero_width_bit_field_p,
+                                 bool vls_p = false, unsigned abi_vlen = 0)
 {
   if (!type || TREE_CODE (type) != RECORD_TYPE)
     return -1;
 
   return riscv_flatten_aggregate_field (type, fields, 0, 0,
-                                       ignore_zero_width_bit_field_p);
+                                       ignore_zero_width_bit_field_p, vls_p,
+                                       abi_vlen);
 }
 
 /* See whether TYPE is a record whose fields should be returned in one or
@@ -6163,6 +6190,9 @@ riscv_pass_vls_aggregate_in_gpr (struct riscv_arg_info 
*info, machine_mode mode,
   return gen_rtx_PARALLEL (mode, gen_rtvec (1, x));
 }
 
+static const predefined_function_abi &
+riscv_fntype_abi_1 (const_tree fntype, bool check_only);
+
 /* Initialize a variable CUM of type CUMULATIVE_ARGS
    for a call to a function whose data type is FNTYPE.
    For a library call, FNTYPE is 0.  */
@@ -6174,7 +6204,7 @@ riscv_init_cumulative_args (CUMULATIVE_ARGS *cum, 
const_tree fntype,
   memset (cum, 0, sizeof (*cum));
 
   if (fntype)
-    cum->variant_cc = (riscv_cc) fntype_abi (fntype).id ();
+    cum->variant_cc = (riscv_cc) riscv_fntype_abi_1 (fntype, /*check_only 
*/true).id ();
   else
     cum->variant_cc = RISCV_CC_BASE;
 }
@@ -6197,7 +6227,7 @@ riscv_hard_regno_nregs (unsigned int regno, machine_mode 
mode);
 
 static rtx
 riscv_get_vector_arg (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
-                     machine_mode mode, bool return_p)
+                     machine_mode mode, bool return_p, bool vls_p = false)
 {
   gcc_assert (riscv_v_ext_mode_p (mode));
 
@@ -6233,8 +6263,9 @@ riscv_get_vector_arg (struct riscv_arg_info *info, const 
CUMULATIVE_ARGS *cum,
   int arg_reg_end = V_ARG_LAST - V_REG_FIRST;
   int aligned_reg_start = ROUND_UP (arg_reg_start, LMUL);
 
-  /* For scalable data and scalable tuple return value.  */
-  if (return_p)
+  /* For scalable data and scalable tuple return value.
+     For VLS CC, we may pass struct like tuple, so need defer the handling.  */
+  if (return_p && !vls_p)
     return gen_rtx_REG (mode, aligned_reg_start + V_REG_FIRST);
 
   /* Iterate through the USED_VRS array to find vector register groups that 
have
@@ -6271,6 +6302,224 @@ riscv_get_vector_arg (struct riscv_arg_info *info, 
const CUMULATIVE_ARGS *cum,
   return NULL_RTX;
 }
 
+
+#define RISCV_ALL_VALID_ABI_VLEN(F) \
+  F (32) \
+  F (64) \
+  F (128) \
+  F (256) \
+  F (512) \
+  F (1024) \
+  F (2048) \
+  F (4096) \
+  F (8192) \
+  F (16384)
+
+/* Return true if CC is a variant of VLS CC.  */
+
+static bool
+riscv_vls_cc_p (riscv_cc cc)
+{
+  switch (cc)
+    {
+#define VLS_CC_ABI_VLEN_CASE(ABI_VLEN) \
+  case RISCV_CC_VLS_V_##ABI_VLEN:
+    RISCV_ALL_VALID_ABI_VLEN(VLS_CC_ABI_VLEN_CASE)
+
+#undef VLS_CC_ABI_VLEN_CASE
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Get ABI_VLEN from cc.  */
+
+static unsigned int
+riscv_get_cc_abi_vlen (riscv_cc cc)
+{
+  switch (cc)
+    {
+#define VLS_CC_ABI_VLEN_CASE(ABI_VLEN) \
+      case RISCV_CC_VLS_V_##ABI_VLEN: \
+       return ABI_VLEN;
+    RISCV_ALL_VALID_ABI_VLEN(VLS_CC_ABI_VLEN_CASE)
+
+#undef VLS_CC_ABI_VLEN_CASE
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Return true if ABI_VLEN is a valid for VLS_CC.  */
+
+static bool
+riscv_valid_abi_vlen_vls_cc_p (unsigned abi_vlen)
+{
+  switch (abi_vlen)
+    {
+#define VLS_CC_ABI_VLEN_CASE(ABI_VLEN) \
+  case ABI_VLEN:
+    RISCV_ALL_VALID_ABI_VLEN(VLS_CC_ABI_VLEN_CASE)
+
+#undef VLS_CC_ABI_VLEN_CASE
+      return true;
+    default:
+      return false;
+    }
+}
+
+static riscv_cc
+riscv_get_riscv_cc_by_abi_vlen (unsigned abi_vlen)
+{
+  switch (abi_vlen)
+    {
+#define VLS_CC_ABI_VLEN_CASE(ABI_VLEN) \
+      case ABI_VLEN: \
+       return RISCV_CC_VLS_V_##ABI_VLEN;
+    RISCV_ALL_VALID_ABI_VLEN(VLS_CC_ABI_VLEN_CASE)
+
+#undef VLS_CC_ABI_VLEN_CASE
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Get a VLS type has same size as MODE in ABI_VLEN, but element is always
+   in integer mode.  */
+
+static machine_mode
+riscv_get_vls_container_type (machine_mode mode, unsigned abi_vlen)
+{
+  machine_mode element_mode = GET_MODE_INNER (mode);
+  unsigned int mode_size = GET_MODE_SIZE (mode).to_constant ();
+  unsigned int lmul = ROUND_UP (mode_size * 8, abi_vlen) / abi_vlen;
+
+  /* Always use integer mode to pass to simplify the logic - we allow pass
+     unsupported vector type in vector register, e.g. float16x4_t even no 
vector
+     fp16 support.  */
+  switch (GET_MODE_SIZE (element_mode).to_constant ())
+    {
+    case 1:
+      element_mode = QImode;
+      break;
+    case 2:
+      element_mode = HImode;
+      break;
+    case 4:
+      element_mode = SImode;
+      break;
+    case 8:
+      element_mode = DImode;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  scalar_mode smode = as_a<scalar_mode> (element_mode);
+  return get_lmul_mode (smode, lmul).require ();
+}
+
+/* Pass VLS type argument in vector argument register.  */
+
+static rtx
+riscv_pass_vls_in_vr (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
+                     machine_mode mode, bool return_p)
+{
+  gcc_assert (riscv_v_ext_vls_mode_p (mode));
+
+  unsigned int abi_vlen = riscv_get_cc_abi_vlen (cum->variant_cc);
+  unsigned int mode_size = GET_MODE_SIZE (mode).to_constant ();
+  unsigned int lmul = ROUND_UP (mode_size * 8, abi_vlen) / abi_vlen;
+
+  /* Put into memory if it need more than 8 registers (> LMUL 8).  */
+  if (lmul > 8)
+    return NULL_RTX;
+
+  machine_mode vla_mode = riscv_get_vls_container_type (mode, abi_vlen);
+  rtx reg = riscv_get_vector_arg (info, cum, vla_mode,
+                                 return_p, /* vls_p */ true);
+
+  /* Can't get vector register to pass, pass by memory.  */
+  if (!reg)
+    return NULL_RTX;
+
+  PUT_MODE (reg, mode);
+
+  return reg;
+}
+
+/* Pass aggregate with VLS type argument in vector argument registers.  */
+
+static rtx
+riscv_pass_aggregate_in_vr (struct riscv_arg_info *info,
+                           const CUMULATIVE_ARGS *cum, const_tree type,
+                           bool return_p)
+{
+  riscv_aggregate_field fields[8];
+  unsigned int abi_vlen = riscv_get_cc_abi_vlen (cum->variant_cc);
+  int i;
+  int n = riscv_flatten_aggregate_argument (type, fields, true,
+                                           /* vls_p */ true, abi_vlen);
+
+  if (n == -1)
+    return NULL_RTX;
+
+  /* Check all field has same size.  */
+  unsigned int mode_size
+    = GET_MODE_SIZE (TYPE_MODE (fields[0].type)).to_constant ();
+  for (int i = 1; i < n; i++)
+    if (GET_MODE_SIZE (TYPE_MODE (fields[i].type)).to_constant () != mode_size)
+      return NULL_RTX; /* Return NULL_RTX if we cannot find a suitable reg.  */
+
+  /* Check total size is <= abi_vlen * 8, we use up to 8 vector register to
+     pass argument.  */
+  if (mode_size * 8 > abi_vlen)
+    return NULL_RTX; /* Return NULL_RTX if we cannot find a suitable reg.  */
+
+  /* Backup cum->used_vrs since we will defer the update until
+     riscv_function_arg_advance.  */
+  CUMULATIVE_ARGS local_cum;
+  memcpy (&local_cum, cum, sizeof (local_cum));
+
+  unsigned num_vrs = 0;
+
+  /* Allocate vector registers for the arguments.  */
+  rtx expr_list[8];
+  for (i = 0; i < n; i++)
+    {
+      machine_mode mode = TYPE_MODE (fields[i].type);
+      machine_mode vla_mode = riscv_get_vls_container_type (mode, abi_vlen);
+      /* Use riscv_get_vector_arg with VLA type to simplify the calling
+        convention implementation.  */
+      rtx reg
+       = riscv_get_vector_arg (info, &local_cum, vla_mode,
+                               return_p, /* vls_p */true);
+
+      /* Can't get vector register to pass, pass by memory.  */
+      if (!reg)
+       return NULL_RTX;
+
+      PUT_MODE (reg, mode);
+
+      expr_list[i]
+       = gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (fields[i].offset));
+
+      num_vrs += info->num_vrs;
+
+      /* Set the corresponding register in USED_VRS to used status.  */
+      for (unsigned int i = 0; i < info->num_vrs; i++)
+       {
+         gcc_assert (!local_cum.used_vrs[info->vr_offset + i]);
+         local_cum.used_vrs[info->vr_offset + i] = true;
+       }
+    }
+
+  info->num_vrs = num_vrs;
+
+  return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (n, expr_list));
+}
+
 /* Fill INFO with information about a single argument, and return an RTL
    pattern to pass or return the argument. Return NULL_RTX if argument cannot
    pass or return in registers, then the argument may be passed by reference or
@@ -6363,7 +6612,17 @@ riscv_get_arg_info (struct riscv_arg_info *info, const 
CUMULATIVE_ARGS *cum,
       if (riscv_vector_type_p (type) && riscv_v_ext_mode_p (mode))
        return riscv_get_vector_arg (info, cum, mode, return_p);
 
-      /* For vls mode aggregated in gpr.  */
+      if (riscv_vls_cc_p (cum->variant_cc))
+       {
+         if (riscv_v_ext_vls_mode_p (mode))
+           return riscv_pass_vls_in_vr (info, cum, mode, return_p);
+
+         rtx ret = riscv_pass_aggregate_in_vr (info, cum, type, return_p);
+         if (ret)
+           return ret;
+       }
+
+      /* For vls mode aggregated in gpr (for non-VLS-CC).  */
       if (riscv_v_ext_vls_mode_p (mode))
        return riscv_pass_vls_aggregate_in_gpr (info, mode, gpr_base);
     }
@@ -6420,7 +6679,8 @@ riscv_function_arg_advance (cumulative_args_t cum_v,
       cum->used_vrs[info.vr_offset + i] = true;
     }
 
-  if ((info.num_vrs > 0 || info.num_mrs > 0) && cum->variant_cc != RISCV_CC_V)
+  if ((info.num_vrs > 0 || info.num_mrs > 0) && cum->variant_cc != RISCV_CC_V
+      && !riscv_vls_cc_p (cum->variant_cc))
     {
       error ("RVV type %qT cannot be passed to an unprototyped function",
             arg.type);
@@ -6532,14 +6792,19 @@ riscv_pass_by_reference (cumulative_args_t cum_v, const 
function_arg_info &arg)
 /* Implement TARGET_RETURN_IN_MEMORY.  */
 
 static bool
-riscv_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED)
+riscv_return_in_memory (const_tree type, const_tree fntype)
 {
   CUMULATIVE_ARGS args;
+
+  if (fntype)
+    riscv_init_cumulative_args (&args, fntype, NULL_RTX, NULL_TREE, 0);
+  else
+    /* The rules for returning in memory are the same as for passing the
+       first named argument by reference.  */
+    memset (&args, 0, sizeof args);
+
   cumulative_args_t cum = pack_cumulative_args (&args);
 
-  /* The rules for returning in memory are the same as for passing the
-     first named argument by reference.  */
-  memset (&args, 0, sizeof args);
   function_arg_info arg (const_cast<tree> (type), /*named=*/true);
   return riscv_pass_by_reference (cum, arg);
 }
@@ -6583,9 +6848,9 @@ riscv_setup_incoming_varargs (cumulative_args_t cum,
 /* Return the descriptor of the Standard Vector Calling Convention Variant.  */
 
 static const predefined_function_abi &
-riscv_v_abi ()
+riscv_v_abi (riscv_cc abi)
 {
-  predefined_function_abi &v_abi = function_abis[RISCV_CC_V];
+  predefined_function_abi &v_abi = function_abis[abi];
   if (!v_abi.initialized_p ())
     {
       HARD_REG_SET full_reg_clobbers
@@ -6595,7 +6860,7 @@ riscv_v_abi ()
        CLEAR_HARD_REG_BIT (full_reg_clobbers, regno);
       for (int regno = V_REG_FIRST + 24; regno <= V_REG_FIRST + 31; regno += 1)
        CLEAR_HARD_REG_BIT (full_reg_clobbers, regno);
-      v_abi.initialize (RISCV_CC_V, full_reg_clobbers);
+      v_abi.initialize (abi, full_reg_clobbers);
     }
   return v_abi;
 }
@@ -6756,13 +7021,14 @@ riscv_validate_vector_type (const_tree type, const char 
*hint)
    RISC-V V registers.  */
 
 static bool
-riscv_return_value_is_vector_type_p (const_tree fntype)
+riscv_return_value_is_vector_type_p (const_tree fntype, bool check_only)
 {
   tree return_type = TREE_TYPE (fntype);
 
   if (riscv_vector_type_p (return_type))
     {
-      riscv_validate_vector_type (return_type, "return type");
+      if (!check_only)
+       riscv_validate_vector_type (return_type, "return type");
       return true;
     }
   else
@@ -6773,7 +7039,7 @@ riscv_return_value_is_vector_type_p (const_tree fntype)
    RISC-V V registers.  */
 
 static bool
-riscv_arguments_is_vector_type_p (const_tree fntype)
+riscv_arguments_is_vector_type_p (const_tree fntype, bool check_only)
 {
   for (tree chain = TYPE_ARG_TYPES (fntype); chain && chain != void_list_node;
        chain = TREE_CHAIN (chain))
@@ -6781,7 +7047,8 @@ riscv_arguments_is_vector_type_p (const_tree fntype)
       tree arg_type = TREE_VALUE (chain);
       if (riscv_vector_type_p (arg_type))
        {
-         riscv_validate_vector_type (arg_type, "argument type");
+         if (!check_only)
+           riscv_validate_vector_type (arg_type, "argument type");
          return true;
        }
     }
@@ -6792,14 +7059,15 @@ riscv_arguments_is_vector_type_p (const_tree fntype)
 /* Return true if FUNC is a riscv_vector_cc function.
    For more details please reference the below link.
    https://github.com/riscv-non-isa/riscv-c-api-doc/pull/67 */
+
 static bool
-riscv_vector_cc_function_p (const_tree fntype)
+riscv_vector_cc_function_p (const_tree fntype, bool check_only)
 {
   tree attr = TYPE_ATTRIBUTES (fntype);
   bool vector_cc_p = lookup_attribute ("vector_cc", attr) != NULL_TREE
     || lookup_attribute ("riscv_vector_cc", attr) != NULL_TREE;
 
-  if (vector_cc_p && !TARGET_VECTOR)
+  if (vector_cc_p && !TARGET_VECTOR && !check_only)
     error_at (input_location,
              "function attribute %qs requires the V ISA extension",
              "riscv_vector_cc");
@@ -6807,26 +7075,91 @@ riscv_vector_cc_function_p (const_tree fntype)
   return vector_cc_p;
 }
 
-/* Implement TARGET_FNTYPE_ABI.  */
+/* Return the riscv_cc value according to the attribute arguments.
+   If the attribute arguments are invalid, return RISCV_CC_UNKNOWN
+   and emit an error message.  */
+
+static riscv_cc
+riscv_get_vls_cc_attr (const_tree args, bool check_only = false)
+{
+  /* Default ABI_VLEN is 128.  */
+  int abi_vlen = 128;
+
+  if (args && TREE_CODE (args) == TREE_LIST)
+    {
+      tree vlen_arg = TREE_VALUE (args);
+      if (vlen_arg && TREE_CODE (vlen_arg) == INTEGER_CST)
+       abi_vlen = TREE_INT_CST_LOW (vlen_arg);
+    }
+
+  if (!riscv_valid_abi_vlen_vls_cc_p (abi_vlen))
+    {
+      error_at (input_location,
+               "unsupported ABI_VLEN value %d for %qs attribute;"
+               "ABI_VLEN must be in the range [32, 16384] and must be "
+               "a power of 2.",
+               "riscv_vls_cc", abi_vlen);
+      return RISCV_CC_UNKNOWN;
+    }
+
+  return riscv_get_riscv_cc_by_abi_vlen (abi_vlen);
+}
+
+/* Return true if FUNC is a riscv_vector_cc function.
+   For more details please reference the below link.
+   https://github.com/riscv-non-isa/riscv-c-api-doc/pull/67 */
+static riscv_cc
+riscv_vls_cc_function_abi (const_tree fntype, bool check_only)
+{
+  tree attr = TYPE_ATTRIBUTES (fntype);
+  bool vls_cc_p = lookup_attribute ("vls_cc", attr) != NULL_TREE
+                 || lookup_attribute ("riscv_vls_cc", attr) != NULL_TREE;
+
+  if (!vls_cc_p)
+    return RISCV_CC_UNKNOWN;
+
+  if (!TARGET_VECTOR && !check_only)
+    error_at (input_location,
+             "function attribute %qs requires the vector ISA extension",
+             "riscv_vls_cc");
+
+  tree args = TREE_VALUE (attr);
+  return riscv_get_vls_cc_attr (args);
+}
+
+/* Implemention of TARGET_FNTYPE_ABI, but one extra parameter `check_only`
+   to suppress warning message */
 
 static const predefined_function_abi &
-riscv_fntype_abi (const_tree fntype)
+riscv_fntype_abi_1 (const_tree fntype, bool check_only)
 {
   /* Implement the vector calling convention.  For more details please
      reference the below link.
      https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/389  */
   bool validate_v_abi_p = false;
 
-  validate_v_abi_p |= riscv_return_value_is_vector_type_p (fntype);
-  validate_v_abi_p |= riscv_arguments_is_vector_type_p (fntype);
-  validate_v_abi_p |= riscv_vector_cc_function_p (fntype);
+  validate_v_abi_p |= riscv_return_value_is_vector_type_p (fntype, check_only);
+  validate_v_abi_p |= riscv_arguments_is_vector_type_p (fntype, check_only);
+  validate_v_abi_p |= riscv_vector_cc_function_p (fntype, check_only);
 
   if (validate_v_abi_p)
-    return riscv_v_abi ();
+    return riscv_v_abi (RISCV_CC_V);
+
+  riscv_cc abi = riscv_vls_cc_function_abi (fntype, check_only);
+  if (abi != RISCV_CC_UNKNOWN)
+    return riscv_v_abi (abi);
 
   return default_function_abi;
 }
 
+/* Implement TARGET_FNTYPE_ABI.  */
+
+static const predefined_function_abi &
+riscv_fntype_abi (const_tree fntype)
+{
+  return riscv_fntype_abi_1 (fntype, /* check_only */false);
+}
+
 /* Return riscv calling convention of call_insn.  */
 riscv_cc
 get_riscv_cc (const rtx use)
@@ -6915,6 +7248,25 @@ riscv_handle_type_attribute (tree *node 
ATTRIBUTE_UNUSED, tree name, tree args,
   return NULL_TREE;
 }
 
+static tree
+riscv_handle_rvv_vls_cc_attribute (tree *node, tree name, tree args,
+                                  ATTRIBUTE_UNUSED int flags,
+                                  bool *no_add_attrs)
+{
+  bool vls_cc_p = is_attribute_p ("vls_cc", name)
+                 || is_attribute_p ("riscv_vls_cc", name);
+
+  if (!vls_cc_p)
+    return NULL_TREE;
+
+  riscv_cc cc = riscv_get_vls_cc_attr (args);
+
+  if (cc == RISCV_CC_UNKNOWN)
+    *no_add_attrs = true;
+
+  return NULL_TREE;
+}
+
 static tree
 riscv_handle_rvv_vector_bits_attribute (tree *node, tree name, tree args,
                                        ATTRIBUTE_UNUSED int flags,
@@ -11082,7 +11434,7 @@ riscv_asm_output_variant_cc (FILE *stream, const tree 
decl, const char *name)
   if (TREE_CODE (decl) == FUNCTION_DECL)
     {
       riscv_cc cc = (riscv_cc) fndecl_abi (decl).id ();
-      if (cc == RISCV_CC_V)
+      if (cc == RISCV_CC_V || riscv_vls_cc_p (cc))
        {
          fprintf (stream, "\t.variant_cc\t");
          assemble_name (stream, name);
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index 9146571908f..a229928677f 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -779,6 +779,17 @@ enum riscv_cc
 {
   RISCV_CC_BASE = 0, /* Base standard RISC-V ABI.  */
   RISCV_CC_V, /* For functions that pass or return values in V registers.  */
+  /* For functions that pass or return values in V registers.  */
+  RISCV_CC_VLS_V_32,
+  RISCV_CC_VLS_V_64,
+  RISCV_CC_VLS_V_128,
+  RISCV_CC_VLS_V_256,
+  RISCV_CC_VLS_V_512,
+  RISCV_CC_VLS_V_1024,
+  RISCV_CC_VLS_V_2048,
+  RISCV_CC_VLS_V_4096,
+  RISCV_CC_VLS_V_8192,
+  RISCV_CC_VLS_V_16384,
   RISCV_CC_UNKNOWN
 };
 
@@ -786,6 +797,8 @@ typedef struct {
   /* The calling convention that current function used.  */
   enum riscv_cc variant_cc;
 
+  unsigned int abi_vlen;
+
   /* Number of integer registers used so far, up to MAX_ARGS_IN_REGISTERS. */
   unsigned int num_gprs;
 
-- 
2.34.1

Reply via email to