[1/3] RISC-V: Part-1: Select suitable vector registers for vector type args and returns

Message ID 20230720090126.2976103-2-lehua.ding@rivai.ai
State Unresolved
Headers
Series RISC-V: Add an experimental vector calling convention |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Lehua Ding July 20, 2023, 9:01 a.m. UTC
  I have posted below the vector register calling convention rules from in the
proposal[1]:

v0 is used to pass the first vector mask argument to a function, and to return
vector mask result from a function. v8-v23 are used to pass vector data
arguments, vector tuple arguments and the rest vector mask arguments to a
function, and to return vector data and vector tuple results from a function.

Each vector data type and vector tuple type has an LMUL attribute that
indicates a vector register group. The value of LMUL indicates the number of
vector registers in the vector register group and requires the first vector
register number in the vector register group must be a multiple of it. For
example, the LMUL of `vint64m8_t` is 8, so v8-v15 vector register group can be
allocated to this type, but v9-v16 can not because the v9 register number is
not a multiple of 8. If LMUL is less than 1, it is treated as 1. If it is a
vector mask type, its LMUL is 1.

Each vector tuple type also has an NFIELDS attribute that indicates how many
vector register groups the type contains. Thus a vector tuple type needs to
take up LMUL×NFIELDS registers.

The rules for passing vector arguments are as follows:

1. For the first vector mask argument, use v0 to pass it. The argument has now
been allocated.

2. For vector data arguments or rest vector mask arguments, starting from the
v8 register, if a vector register group between v8-v23 that has not been
allocated can be found and the first register number is a multiple of LMUL,
then allocate this vector register group to the argument and mark these
registers as allocated. Otherwise, pass it by reference. The argument has now
been allocated.

3. For vector tuple arguments, starting from the v8 register, if NFIELDS
consecutive vector register groups between v8-v23 that have not been allocated
can be found and the first register number is a multiple of LMUL, then allocate
these vector register groups to the argument and mark these registers as
allocated. Otherwise, pass it by reference. The argument has now been allocated.

NOTE: It should be stressed that the search for the appropriate vector register
groups starts at v8 each time and does not start at the next register after the
registers are allocated for the previous vector argument. Therefore, it is
possible that the vector register number allocated to a vector argument can be
less than the vector register number allocated to previous vector arguments.
For example, for the function
`void foo (vint32m1_t a, vint32m2_t b, vint32m1_t c)`, according to the rules
of allocation, v8 will be allocated to `a`, v10-v11 will be allocated to `b`
and v9 will be allocated to `c`. This approach allows more vector registers to
be allocated to arguments in some cases.

Vector values are returned in the same manner as the first named argument of
the same type would be passed.

[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/389

gcc/ChangeLog:

	* config/riscv/riscv-protos.h (builtin_type_p): New function for checking vector type.
	* config/riscv/riscv-vector-builtins.cc (builtin_type_p): Ditto.
	* config/riscv/riscv.cc (struct riscv_arg_info): New fields.
	(riscv_init_cumulative_args): Setup variant_cc field.
	(riscv_vector_type_p): New function for checking vector type.
	(riscv_hard_regno_nregs): Hoist declare.
	(riscv_get_vector_arg): Subroutine of riscv_get_arg_info.
	(riscv_get_arg_info): Support vector cc.
	(riscv_function_arg_advance): Update cum.
	(riscv_pass_by_reference): Handle vector args.
	(riscv_v_abi): New function return vector abi.
	(riscv_return_value_is_vector_type_p): New function for check vector arguments.
	(riscv_arguments_is_vector_type_p): New function for check vector returns.
	(riscv_fntype_abi): Implement TARGET_FNTYPE_ABI.
	(TARGET_FNTYPE_ABI): Implement TARGET_FNTYPE_ABI.
	* config/riscv/riscv.h (GCC_RISCV_H): Define macros for vector abi.
	(MAX_ARGS_IN_VECTOR_REGISTERS): Ditto.
	(MAX_ARGS_IN_MASK_REGISTERS): Ditto.
	(V_ARG_FIRST): Ditto.
	(V_ARG_LAST): Ditto.
	(enum riscv_cc): Define all RISCV_CC variants.
	* config/riscv/riscv.opt: Add --param=riscv-vector-abi.

gcc/testsuite/ChangeLog:

	* gcc.target/riscv/rvv/base/abi-call-args-1-run.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-args-1.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-args-2-run.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-args-2.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-args-3-run.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-args-3.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-args-4-run.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-args-4.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-error-1.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-return-run.c: New test.
	* gcc.target/riscv/rvv/base/abi-call-return.c: New test.

---
 gcc/config/riscv/riscv-protos.h               |   1 +
 gcc/config/riscv/riscv-vector-builtins.cc     |  10 +
 gcc/config/riscv/riscv.cc                     | 240 ++++++++++++++--
 gcc/config/riscv/riscv.h                      |  25 ++
 gcc/config/riscv/riscv.opt                    |   5 +
 .../riscv/rvv/base/abi-call-args-1-run.c      | 127 +++++++++
 .../riscv/rvv/base/abi-call-args-1.c          | 197 +++++++++++++
 .../riscv/rvv/base/abi-call-args-2-run.c      |  34 +++
 .../riscv/rvv/base/abi-call-args-2.c          |  27 ++
 .../riscv/rvv/base/abi-call-args-3-run.c      | 260 ++++++++++++++++++
 .../riscv/rvv/base/abi-call-args-3.c          | 116 ++++++++
 .../riscv/rvv/base/abi-call-args-4-run.c      | 145 ++++++++++
 .../riscv/rvv/base/abi-call-args-4.c          | 111 ++++++++
 .../riscv/rvv/base/abi-call-error-1.c         |  11 +
 .../riscv/rvv/base/abi-call-return-run.c      | 127 +++++++++
 .../riscv/rvv/base/abi-call-return.c          | 197 +++++++++++++
 16 files changed, 1616 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-1-run.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-2-run.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-3-run.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-4-run.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-error-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-return-run.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-return.c
  

Comments

Kito Cheng Aug. 7, 2023, 8:51 a.m. UTC | #1
Thanks for the high quality patch, it's generally LGTM, only a few
minor comments :)

> +  /* The number and alignment of vector registers need for this scalable vector
> +     argument. When the mode size is less than a full vector, we use 1 vector
> +     register to pass. Just call TARGET_HARD_REGNO_NREGS for the number
> +     infomation.  */

gmail told me here is a typo: information :P

> +  int nregs = riscv_hard_regno_nregs (V_ARG_FIRST, mode);
> +  int LMUL = riscv_v_ext_tuple_mode_p (mode)
> +              ? nregs / riscv_vector::get_nf (mode)
> +              : nregs;
> +  int arg_reg_start = V_ARG_FIRST - V_REG_FIRST;
> +  int arg_reg_end = V_ARG_LAST - V_REG_FIRST;
> +  int aligned_reg_start = (arg_reg_start + LMUL - 1) & -LMUL;

Use` ROUND_UP (arg_reg_start, LMUL)`
  
Lehua Ding Aug. 10, 2023, 7:08 a.m. UTC | #2
Thanks so much for Kito's online and offline comments.
I have upload V2 patchs which address all comments.


https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626935.html


Best,
Lehua




------------------ Original ------------------
From:                                                                                                                        "Kito Cheng"                                                                                    <kito.cheng@gmail.com&gt;;
Date:&nbsp;Mon, Aug 7, 2023 04:51 PM
To:&nbsp;"Lehua Ding"<lehua.ding@rivai.ai&gt;;
Cc:&nbsp;"gcc-patches"<gcc-patches@gcc.gnu.org&gt;;"juzhe.zhong"<juzhe.zhong@rivai.ai&gt;;"rdapp.gcc"<rdapp.gcc@gmail.com&gt;;"palmer"<palmer@rivosinc.com&gt;;"jeffreyalaw"<jeffreyalaw@gmail.com&gt;;
Subject:&nbsp;Re: [PATCH 1/3] RISC-V: Part-1: Select suitable vector registers for vector type args and returns



Thanks for the high quality patch, it's generally LGTM, only a few
minor comments :)

&gt; +&nbsp; /* The number and alignment of vector registers need for this scalable vector
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; argument. When the mode size is less than a full vector, we use 1 vector
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; register to pass. Just call TARGET_HARD_REGNO_NREGS for the number
&gt; +&nbsp;&nbsp;&nbsp;&nbsp; infomation.&nbsp; */

gmail told me here is a typo: information :P

&gt; +&nbsp; int nregs = riscv_hard_regno_nregs (V_ARG_FIRST, mode);
&gt; +&nbsp; int LMUL = riscv_v_ext_tuple_mode_p (mode)
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ? nregs / riscv_vector::get_nf (mode)
&gt; +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : nregs;
&gt; +&nbsp; int arg_reg_start = V_ARG_FIRST - V_REG_FIRST;
&gt; +&nbsp; int arg_reg_end = V_ARG_LAST - V_REG_FIRST;
&gt; +&nbsp; int aligned_reg_start = (arg_reg_start + LMUL - 1) &amp; -LMUL;

Use` ROUND_UP (arg_reg_start, LMUL)`
  

Patch

diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 16fb8dabca0..52e15e1b5d6 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -221,6 +221,7 @@  enum avl_type
 /* Routines implemented in riscv-vector-builtins.cc.  */
 void init_builtins (void);
 const char *mangle_builtin_type (const_tree);
+bool builtin_type_p (const_tree);
 #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,
diff --git a/gcc/config/riscv/riscv-vector-builtins.cc b/gcc/config/riscv/riscv-vector-builtins.cc
index 528dca7ae85..2f66d99cfc4 100644
--- a/gcc/config/riscv/riscv-vector-builtins.cc
+++ b/gcc/config/riscv/riscv-vector-builtins.cc
@@ -3961,6 +3961,16 @@  mangle_builtin_type (const_tree type)
   return NULL;
 }
 
+/* Return true if TYPE is a built-in RVV type defined by the ABI.  */
+bool
+builtin_type_p (const_tree type)
+{
+  if (!type)
+    return false;
+
+  return lookup_vector_type_attribute (type);
+}
+
 /* Initialize all compiler built-ins related to RVV that should be
    defined at start-up.  */
 void
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 332fa720f01..73e9f2001e6 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -170,6 +170,18 @@  struct riscv_arg_info {
 
   /* The offset of the first register used, provided num_fprs is nonzero.  */
   unsigned int fpr_offset;
+
+  /* The number of vector registers allocated to this argument.  */
+  unsigned int num_vrs;
+
+  /* The offset of the first register used, provided num_vrs is nonzero.  */
+  unsigned int vr_offset;
+
+  /* The number of mask registers allocated to this argument.  */
+  unsigned int num_mrs;
+
+  /* The offset of the first register used, provided num_mrs is nonzero.  */
+  unsigned int mr_offset;
 };
 
 /* One stage in a constant building sequence.  These sequences have
@@ -3887,6 +3899,11 @@  riscv_init_cumulative_args (CUMULATIVE_ARGS *cum,
 {
   memset (cum, 0, sizeof (*cum));
 
+  if (fntype)
+    cum->variant_cc = (riscv_cc) fntype_abi (fntype).id ();
+  else
+    cum->variant_cc = RISCV_CC_BASE;
+
   if (fndecl)
     {
       const tree_function_decl &fn
@@ -3897,12 +3914,106 @@  riscv_init_cumulative_args (CUMULATIVE_ARGS *cum,
     }
 }
 
-/* Fill INFO with information about a single argument, and return an
-   RTL pattern to pass or return the argument.  CUM is the cumulative
-   state for earlier arguments.  MODE is the mode of this argument and
-   TYPE is its type (if known).  NAMED is true if this is a named
-   (fixed) argument rather than a variable one.  RETURN_P is true if
-   returning the argument, or false if passing the argument.  */
+/* Return true if TYPE is a vector type that can be passed in vector registers.
+ */
+
+static bool
+riscv_vector_type_p (const_tree type)
+{
+  /* Currently, only builtin scalabler vector type is allowed, in the future,
+     more vector types may be allowed, such as GNU vector type, etc.  */
+  return riscv_vector::builtin_type_p (type);
+}
+
+static unsigned int
+riscv_hard_regno_nregs (unsigned int regno, machine_mode mode);
+
+/* Subroutine of riscv_get_arg_info.  */
+
+static rtx
+riscv_get_vector_arg (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
+		      machine_mode mode, bool return_p)
+{
+  gcc_assert (riscv_v_ext_mode_p (mode));
+
+  info->mr_offset = cum->num_mrs;
+  if (GET_MODE_CLASS (mode) == MODE_VECTOR_BOOL)
+    {
+      /* For scalable mask return value.  */
+      if (return_p)
+	return gen_rtx_REG (mode, V_REG_FIRST);
+
+      /* For the first scalable mask argument.  */
+      if (info->mr_offset < MAX_ARGS_IN_MASK_REGISTERS)
+	{
+	  info->num_mrs = 1;
+	  return gen_rtx_REG (mode, V_REG_FIRST);
+	}
+      else
+	{
+	  /* Rest scalable mask arguments are treated as scalable data
+	     arguments.  */
+	}
+    }
+
+  /* The number and alignment of vector registers need for this scalable vector
+     argument. When the mode size is less than a full vector, we use 1 vector
+     register to pass. Just call TARGET_HARD_REGNO_NREGS for the number
+     infomation.  */
+  int nregs = riscv_hard_regno_nregs (V_ARG_FIRST, mode);
+  int LMUL = riscv_v_ext_tuple_mode_p (mode)
+	       ? nregs / riscv_vector::get_nf (mode)
+	       : nregs;
+  int arg_reg_start = V_ARG_FIRST - V_REG_FIRST;
+  int arg_reg_end = V_ARG_LAST - V_REG_FIRST;
+  int aligned_reg_start = (arg_reg_start + LMUL - 1) & -LMUL;
+
+  /* For scalable data and scalable tuple return value.  */
+  if (return_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
+     not been allocated and the first register is aligned with LMUL.  */
+  for (int i = aligned_reg_start; i + nregs - 1 <= arg_reg_end; i += LMUL)
+    {
+      /* The index in USED_VRS array.  */
+      int idx = i - arg_reg_start;
+      /* Find the first register unused.  */
+      if (!cum->used_vrs[idx])
+	{
+	  bool find_set = true;
+	  /* Check there are NREGS continuous unused registers are not
+	     allocated.  */
+	  for (int j = 1; j < nregs; j++)
+	    if (cum->used_vrs[idx + j])
+	      {
+		find_set = false;
+		/* Update I to the last aligned register which
+		   cannot be used and the next iteration will add
+		   LMUL step to I.  */
+		i += (j / LMUL) * LMUL;
+		break;
+	      }
+
+	  if (find_set)
+	    {
+	      info->num_vrs = nregs;
+	      info->vr_offset = idx;
+	      return gen_rtx_REG (mode, i + V_REG_FIRST);
+	    }
+	}
+    }
+
+  return NULL_RTX;
+}
+
+/* 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
+   through the stack or  .  CUM is the cumulative state for earlier arguments.
+   MODE is the mode of this argument and TYPE is its type (if known). NAMED is
+   true if this is a named (fixed) argument rather than a variable one. RETURN_P
+   is true if returning the argument, or false if passing the argument.  */
 
 static rtx
 riscv_get_arg_info (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
@@ -3924,11 +4035,10 @@  riscv_get_arg_info (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
       riscv_pass_in_vector_p (type);
     }
 
-  /* All current vector arguments and return values are passed through the
-     function stack. Ideally, we should either warn the user not to use an RVV
-     vector type as function argument or support a calling convention
-     with better performance.  */
-  if (riscv_v_ext_mode_p (mode))
+  /* When vector abi disabled(without --param=riscv-vector-abi option) or
+     scalable vector argument is anonymous, this argument is passed by
+     reference. */
+  if (riscv_v_ext_mode_p (mode) && (!riscv_vector_abi || !named))
     return NULL_RTX;
 
   if (named)
@@ -3992,6 +4102,19 @@  riscv_get_arg_info (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
 				      gregno, TYPE_MODE (fields[1].type),
 				      fields[1].offset);
 	}
+
+      /* Implementing an experimental vector calling convention, the proposal
+	 can be viewed at bellow link:
+	   https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/389
+
+	 You can enable this feature via the `--param=riscv-vector-abi` compiler
+	 option.  */
+      if (riscv_vector_abi)
+	{
+	  /* For scalable vector argument.  */
+	  if (riscv_vector_type_p (type) && riscv_v_ext_mode_p (mode))
+	    return riscv_get_vector_arg (info, cum, mode, return_p);
+	}
     }
 
   /* Work out the size of the argument.  */
@@ -4038,12 +4161,28 @@  riscv_function_arg_advance (cumulative_args_t cum_v,
 
   riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false);
 
+  /* Set the corresponding register in USED_VRS to used status.  */
+  for (unsigned int i = 0; i < info.num_vrs; i++)
+    {
+      gcc_assert (!cum->used_vrs[info.vr_offset + i]);
+      cum->used_vrs[info.vr_offset + i] = true;
+    }
+
+  if ((info.num_vrs > 0 || info.num_mrs > 0) && cum->variant_cc != RISCV_CC_V)
+    {
+      error ("RVV type %qT cannot be passed to an unprototyped function",
+	     arg.type);
+      /* Avoid repeating the message */
+      cum->variant_cc = RISCV_CC_V;
+    }
+
   /* Advance the register count.  This has the effect of setting
      num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned
      argument required us to skip the final GPR and pass the whole
      argument on the stack.  */
   cum->num_fprs = info.fpr_offset + info.num_fprs;
   cum->num_gprs = info.gpr_offset + info.num_gprs;
+  cum->num_mrs = info.mr_offset + info.num_mrs;
 }
 
 /* Implement TARGET_ARG_PARTIAL_BYTES.  */
@@ -4105,20 +4244,23 @@  riscv_pass_by_reference (cumulative_args_t cum_v, const function_arg_info &arg)
   CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
 
   /* ??? std_gimplify_va_arg_expr passes NULL for cum.  Fortunately, we
-     never pass variadic arguments in floating-point registers, so we can
-     avoid the call to riscv_get_arg_info in this case.  */
+     never pass variadic arguments in floating-point and vector registers,
+     so we can avoid the call to riscv_get_arg_info in this case.  */
   if (cum != NULL)
     {
       /* Don't pass by reference if we can use a floating-point register.  */
       riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false);
       if (info.num_fprs)
 	return false;
+
+      /* Don't pass by reference if we can use vector register groups.  */
+      if (info.num_vrs > 0 || info.num_mrs > 0)
+	return false;
     }
 
-  /* All current vector arguments and return values are passed through the
-     function stack. Ideally, we should either warn the user not to use an RVV
-     vector type as function argument or support a calling convention
-     with better performance.  */
+  /* When vector abi disabled(without --param=riscv-vector-abi option) or
+     scalable vector argument is anonymous or cannot be passed through vector
+     registers, this argument is passed by reference. */
   if (riscv_v_ext_mode_p (arg.mode))
     return true;
 
@@ -4176,6 +4318,67 @@  riscv_setup_incoming_varargs (cumulative_args_t cum,
     cfun->machine->varargs_size = gp_saved * UNITS_PER_WORD;
 }
 
+/* Return the descriptor of the Standard Vector Calling Convention Variant.  */
+
+static const predefined_function_abi &
+riscv_v_abi ()
+{
+  predefined_function_abi &v_abi = function_abis[RISCV_CC_V];
+  if (!v_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers
+	= default_function_abi.full_reg_clobbers ();
+      /* Callee-saved vector registers: v1-v7, v24-v31.  */
+      for (int regno = V_REG_FIRST + 1; regno <= V_REG_FIRST + 7; regno += 1)
+	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);
+    }
+  return v_abi;
+}
+
+/* Return true if a function with type FNTYPE returns its value in
+   RISC-V V registers.  */
+
+static bool
+riscv_return_value_is_vector_type_p (const_tree fntype)
+{
+  tree return_type = TREE_TYPE (fntype);
+
+  return riscv_vector_type_p (return_type);
+}
+
+/* Return true if a function with type FNTYPE takes arguments in
+   RISC-V V registers.  */
+
+static bool
+riscv_arguments_is_vector_type_p (const_tree fntype)
+{
+  for (tree chain = TYPE_ARG_TYPES (fntype); chain && chain != void_list_node;
+       chain = TREE_CHAIN (chain))
+    {
+      tree arg_type = TREE_VALUE (chain);
+      if (riscv_vector_type_p (arg_type))
+	return true;
+    }
+
+  return false;
+}
+
+/* Implement TARGET_FNTYPE_ABI.  */
+
+static const predefined_function_abi &
+riscv_fntype_abi (const_tree fntype)
+{
+  if (riscv_vector_abi
+      && (riscv_return_value_is_vector_type_p (fntype)
+	  || riscv_arguments_is_vector_type_p (fntype)))
+    return riscv_v_abi ();
+
+  return default_function_abi;
+}
+
 /* Handle an attribute requiring a FUNCTION_DECL;
    arguments as in struct attribute_spec.handler.  */
 static tree
@@ -8279,6 +8482,9 @@  riscv_preferred_else_value (unsigned, tree, unsigned int nops, tree *ops)
 #undef TARGET_PREFERRED_ELSE_VALUE
 #define TARGET_PREFERRED_ELSE_VALUE riscv_preferred_else_value
 
+#undef TARGET_FNTYPE_ABI
+#define TARGET_FNTYPE_ABI riscv_fntype_abi
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-riscv.h"
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index 643e7ea7330..b24b240dd75 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -22,6 +22,7 @@  along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_RISCV_H
 #define GCC_RISCV_H
 
+#include <stdbool.h>
 #include "config/riscv/riscv-opts.h"
 
 /* Target CPU builtins.  */
@@ -643,6 +644,9 @@  enum reg_class
 
 #define MAX_ARGS_IN_REGISTERS (riscv_abi == ABI_ILP32E ? 6 : 8)
 
+#define MAX_ARGS_IN_VECTOR_REGISTERS (16)
+#define MAX_ARGS_IN_MASK_REGISTERS (1)
+
 /* Symbolic macros for the first/last argument registers.  */
 
 #define GP_ARG_FIRST (GP_REG_FIRST + 10)
@@ -650,6 +654,8 @@  enum reg_class
 #define GP_TEMP_FIRST (GP_REG_FIRST + 5)
 #define FP_ARG_FIRST (FP_REG_FIRST + 10)
 #define FP_ARG_LAST  (FP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1)
+#define V_ARG_FIRST (V_REG_FIRST + 8)
+#define V_ARG_LAST (V_ARG_FIRST + MAX_ARGS_IN_VECTOR_REGISTERS - 1)
 
 #define CALLEE_SAVED_REG_NUMBER(REGNO)			\
   ((REGNO) >= 8 && (REGNO) <= 9 ? (REGNO) - 8 :		\
@@ -671,7 +677,19 @@  enum reg_class
   (IN_RANGE ((N), GP_ARG_FIRST, GP_ARG_LAST)				\
    || (UNITS_PER_FP_ARG && IN_RANGE ((N), FP_ARG_FIRST, FP_ARG_LAST)))
 
+/* Define the standard RISC-V calling convention and variants.  */
+
+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.  */
+  RISCV_CC_UNKNOWN
+};
+
 typedef struct {
+  /* The calling convention that current function used.  */
+  enum riscv_cc variant_cc;
+
   /* Number of integer registers used so far, up to MAX_ARGS_IN_REGISTERS. */
   unsigned int num_gprs;
 
@@ -679,6 +697,13 @@  typedef struct {
   unsigned int num_fprs;
 
   int rvv_psabi_warning;
+
+  /* Number of mask registers used so far, up to MAX_ARGS_IN_MASK_REGISTERS.  */
+  unsigned int num_mrs;
+
+  /* The used state of args in vector registers, true for used by prev arg,
+     initial to false.  */
+  bool used_vrs[MAX_ARGS_IN_VECTOR_REGISTERS];
 } CUMULATIVE_ARGS;
 
 /* Initialize a variable CUM of type CUMULATIVE_ARGS
diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
index dd062f1c8bd..cd570480e71 100644
--- a/gcc/config/riscv/riscv.opt
+++ b/gcc/config/riscv/riscv.opt
@@ -301,3 +301,8 @@  Enum(riscv_autovec_lmul) String(m8) Value(RVV_M8)
 -param=riscv-autovec-lmul=
 Target RejectNegative Joined Enum(riscv_autovec_lmul) Var(riscv_autovec_lmul) Init(RVV_M1)
 -param=riscv-autovec-lmul=<string>	Set the RVV LMUL of auto-vectorization in the RISC-V port.
+
+-param=riscv-vector-abi
+Target Undocumented Bool Var(riscv_vector_abi) Init(0)
+Enable the use of vector registers for function arguments and return value.
+This is an experimental switch and may be subject to change in the future.
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-1-run.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-1-run.c
new file mode 100644
index 00000000000..60407278a5b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-1-run.c
@@ -0,0 +1,127 @@ 
+/* { dg-do run } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-additional-sources abi-call-args-1.c } */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "riscv_vector.h"
+
+#define FOO_(TYPE) void foo_##TYPE (TYPE val, TYPE *out);
+
+FOO_ (vbool1_t)
+FOO_ (vbool2_t)
+FOO_ (vbool4_t)
+FOO_ (vbool8_t)
+FOO_ (vbool16_t)
+FOO_ (vbool32_t)
+FOO_ (vbool64_t)
+FOO_ (vint8mf8_t)
+FOO_ (vint8mf4_t)
+FOO_ (vint8mf2_t)
+FOO_ (vint8m1_t)
+FOO_ (vint8m2_t)
+FOO_ (vint8m4_t)
+FOO_ (vint8m8_t)
+FOO_ (vint8m1x5_t)
+FOO_ (vint8m1x8_t)
+FOO_ (vint8m2x3_t)
+FOO_ (vint8m2x4_t)
+FOO_ (vint8m4x2_t)
+
+bool
+check_mask (int8_t *test_data, int8_t *golden_data, size_t vl)
+{
+  size_t i = 0;
+  for (; i + 8 <= vl; i += 8)
+    {
+      if (test_data[i / 8] != golden_data[i / 8])
+	{
+	  printf ("mask diff %lu: %d, %d\n", i / 8, test_data[i / 8],
+		  golden_data[i / 8]);
+	  return false;
+	}
+    }
+  if (vl % 8 != 0)
+    {
+      if ((test_data[i / 8] << (8 - (vl % 8)))
+	  != (golden_data[i / 8] << (8 - (vl % 8))))
+	{
+	  printf ("mask tail diff %lu, tail %d: %d, %d\n", i / 8, vl % 8,
+		  test_data[i / 8], golden_data[i / 8]);
+	  return false;
+	}
+    }
+  return true;
+}
+
+bool
+check_data (int8_t *test_data, int8_t *golden_data, size_t vl)
+{
+  for (size_t i = 0; i < vl; i += 1)
+    {
+      if (test_data[i] != golden_data[i])
+	{
+	  printf ("data diff %lu: %d, %d\n", i, test_data[i], golden_data[i]);
+	  return false;
+	}
+    }
+  return true;
+}
+
+#define INIT_DATA                                                              \
+  size_t vlmax_e8m8 = __riscv_vsetvlmax_e8m8 ();                               \
+  int8_t golden_data[vlmax_e8m8];                                              \
+  memset (golden_data, 0, vlmax_e8m8 * sizeof (int8_t));                       \
+  int8_t test_data[vlmax_e8m8];                                                \
+  memset (test_data, 0, vlmax_e8m8 * sizeof (int8_t));                         \
+  for (size_t i = 0; i < vlmax_e8m8; i += 1)                                   \
+    golden_data[i] = vlmax_e8m8 - 1;
+
+#define FOO_MASK(TYPE, VL)                                                     \
+  {                                                                            \
+    INIT_DATA                                                                  \
+    for (size_t i = 0; i < vlmax_e8m8; i += 1)                                 \
+      test_data[i] = 0;                                                        \
+    TYPE val = *(TYPE *) golden_data;                                          \
+    foo_##TYPE (val, (TYPE *) test_data);                                      \
+    if (!check_mask (test_data, golden_data, VL))                              \
+      abort ();                                                                \
+  }
+
+#define FOO_DATA(TYPE, VL)                                                     \
+  {                                                                            \
+    INIT_DATA                                                                  \
+    for (size_t i = 0; i < vlmax_e8m8; i += 1)                                 \
+      test_data[i] = 0;                                                        \
+    TYPE val = *(TYPE *) golden_data;                                          \
+    foo_##TYPE (val, (TYPE *) test_data);                                      \
+    if (!check_data (test_data, golden_data, VL))                              \
+      abort ();                                                                \
+  }
+
+int
+main ()
+{
+  size_t vlmax = __riscv_vsetvlmax_e8mf8 ();
+  FOO_MASK (vbool1_t, vlmax * 64)
+  FOO_MASK (vbool2_t, vlmax * 32)
+  FOO_MASK (vbool4_t, vlmax * 16)
+  FOO_MASK (vbool8_t, vlmax * 8)
+  FOO_MASK (vbool16_t, vlmax * 4)
+  FOO_MASK (vbool32_t, vlmax * 2)
+  FOO_MASK (vbool64_t, vlmax)
+  FOO_DATA (vint8mf8_t, vlmax)
+  FOO_DATA (vint8mf4_t, vlmax * 2)
+  FOO_DATA (vint8mf2_t, vlmax * 4)
+  FOO_DATA (vint8m1_t, vlmax * 8)
+  FOO_DATA (vint8m2_t, vlmax * 16)
+  FOO_DATA (vint8m4_t, vlmax * 32)
+  FOO_DATA (vint8m8_t, vlmax * 64)
+  FOO_DATA (vint8m1x5_t, vlmax * 8 * 5)
+  FOO_DATA (vint8m1x8_t, vlmax * 8 * 8)
+  FOO_DATA (vint8m2x3_t, vlmax * 16 * 3)
+  FOO_DATA (vint8m2x4_t, vlmax * 16 * 4)
+  FOO_DATA (vint8m4x2_t, vlmax * 32 * 2)
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-1.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-1.c
new file mode 100644
index 00000000000..40560fccfc6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-1.c
@@ -0,0 +1,197 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "riscv_vector.h"
+
+#define FOO_(TYPE)                                                             \
+  void foo_##TYPE (TYPE val, TYPE *out)                                        \
+  {                                                                            \
+    *out = val;                                                                \
+  }
+
+/* Test the first vector mask type argument */
+
+/*
+** foo_vbool1_t:
+**   ...
+**   vsm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool1_t)
+
+/*
+** foo_vbool2_t:
+**   ...
+**   vsm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool2_t)
+
+/*
+** foo_vbool4_t:
+**   ...
+**   vsm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool4_t)
+
+/*
+** foo_vbool8_t:
+**   ...
+**   vsm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool8_t)
+
+/*
+** foo_vbool16_t:
+**   ...
+**   vsm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool16_t)
+
+/*
+** foo_vbool32_t:
+**   ...
+**   vsm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool32_t)
+
+/*
+** foo_vbool64_t:
+**   ...
+**   vsm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool64_t)
+
+/* Test the first vector data type argument */
+
+/*
+** foo_vint8mf8_t:
+**   ...
+**   vse8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8mf8_t)
+
+/*
+** foo_vint8mf4_t:
+**   ...
+**   vse8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8mf4_t)
+
+/*
+** foo_vint8mf2_t:
+**   ...
+**   vse8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8mf2_t)
+
+/*
+** foo_vint8m1_t:
+**   vs1r\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8m1_t)
+
+/*
+** foo_vint8m2_t:
+**   vs2r\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8m2_t)
+
+/*
+** foo_vint8m4_t:
+**   vs4r\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8m4_t)
+
+/*
+** foo_vint8m8_t:
+**   vs8r\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8m8_t)
+
+/*
+** foo_vint8m1x5_t:
+**   ...
+**   vs1r\.v\tv8,0\(a0\)
+**   ...
+**   vs1r\.v\tv9,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv10,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv11,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv12,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m1x5_t)
+
+/*
+** foo_vint8m1x8_t:
+**   ...
+**   vs1r\.v\tv8,0\(a0\)
+**   ...
+**   vs1r\.v\tv9,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv10,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv11,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv12,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv13,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv14,0\(a\d+\)
+**   ...
+**   vs1r\.v\tv15,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m1x8_t)
+
+/*
+** foo_vint8m2x3_t:
+**   ...
+**   vs2r\.v\tv8,0\(a0\)
+**   ...
+**   vs2r\.v\tv10,0\(a\d+\)
+**   ...
+**   vs2r\.v\tv12,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m2x3_t)
+
+/*
+** foo_vint8m2x4_t:
+**   ...
+**   vs2r\.v\tv8,0\(a0\)
+**   ...
+**   vs2r\.v\tv10,0\(a\d+\)
+**   ...
+**   vs2r\.v\tv12,0\(a\d+\)
+**   ...
+**   vs2r\.v\tv14,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m2x4_t)
+
+/*
+** foo_vint8m4x2_t:
+**   ...
+**   vs4r\.v\tv8,0\(a0\)
+**   ...
+**   vs4r\.v\tv12,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m4x2_t)
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-2-run.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-2-run.c
new file mode 100644
index 00000000000..0cb7f7479ed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-2-run.c
@@ -0,0 +1,34 @@ 
+/* { dg-do run } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-additional-sources abi-call-args-2.c } */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include "riscv_vector.h"
+
+int8_t
+va_callee (int count, ...);
+
+bool __attribute__ ((noinline)) va_caller ()
+{
+  size_t vlmax = __riscv_vsetvlmax_e8m1 ();
+  vint8m1_t a1 = __riscv_vmv_v_x_i8m1 (1, vlmax);
+  vint8m1_t a2 = __riscv_vmv_v_x_i8m1 (2, vlmax);
+  vint8m1_t a3 = __riscv_vmv_v_x_i8m1 (3, vlmax);
+  vint8m1_t a4 = __riscv_vmv_v_x_i8m1 (4, vlmax);
+  vint8m1_t a5 = __riscv_vmv_v_x_i8m1 (5, vlmax);
+  vint8m1_t a6 = __riscv_vmv_v_x_i8m1 (6, vlmax);
+  vint8m1_t a7 = __riscv_vmv_v_x_i8m1 (7, vlmax);
+  vint8m1_t a8 = __riscv_vmv_v_x_i8m1 (8, vlmax);
+  int8_t sum = va_callee (8, a1, a2, a3, a4, a5, a6, a7, a8);
+
+  return sum == (int8_t) vlmax * (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8);
+}
+
+int
+main ()
+{
+  if (va_caller ())
+    abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-2.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-2.c
new file mode 100644
index 00000000000..6352f2e594a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-2.c
@@ -0,0 +1,27 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+
+#include <stdarg.h>
+#include "riscv_vector.h"
+
+int8_t
+va_callee (int count, ...)
+{
+  size_t vlmax = __riscv_vsetvlmax_e8m1 ();
+  vint8m1_t sum = __riscv_vmv_v_x_i8m1 (0, vlmax);
+  va_list ap;
+  va_start (ap, count);
+  for (int i = count; i > 0; i--)
+    {
+      vint8m1_t arg = va_arg (ap, vint8m1_t);
+      sum = __riscv_vredsum_vs_i8m1_i8m1 (arg, sum, vlmax);
+    }
+  va_end (ap);
+  return __riscv_vmv_x_s_i8m1_i8 (sum);
+}
+
+/* Make sure the variadic arguments is not passed through the vector register.
+ */
+/* { dg-final { scan-assembler-not {vs[0-9]+r} } } */
+/* { dg-final { scan-assembler-not {vsm} } } */
+/* { dg-final { scan-assembler-not {vse[0-9]+} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-3-run.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-3-run.c
new file mode 100644
index 00000000000..3b1da236268
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-3-run.c
@@ -0,0 +1,260 @@ 
+/* { dg-do run } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-additional-sources abi-call-args-3.c } */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "riscv_vector.h"
+
+#define INIT_DATA                                                              \
+  size_t vlmax_e8m8 = __riscv_vsetvlmax_e8m8 ();                               \
+  int8_t golden_data[vlmax_e8m8];                                              \
+  memset (golden_data, 0, vlmax_e8m8 * sizeof (int8_t));                       \
+  int8_t test_data[vlmax_e8m8];                                                \
+  memset (test_data, 0, vlmax_e8m8 * sizeof (int8_t));                         \
+  for (size_t i = 0; i < vlmax_e8m8; i += 1)                                   \
+    golden_data[i] = vlmax_e8m8 - 1;                                           \
+  int8_t dummy_data[vlmax_e8m8];                                               \
+  for (size_t i = 0; i < vlmax_e8m8; i += 1)                                   \
+    dummy_data[i] = -1;
+
+bool
+check_mask (int8_t *test_data, int8_t *golden_data, size_t vl)
+{
+  size_t i = 0;
+  for (; i + 8 <= vl; i += 8)
+    {
+      if (test_data[i / 8] != golden_data[i / 8])
+	{
+	  printf ("mask diff %lu: %d, %d\n", i / 8, test_data[i / 8],
+		  golden_data[i / 8]);
+	  return false;
+	}
+    }
+  if (vl % 8 != 0)
+    {
+      if ((test_data[i / 8] << (8 - (vl % 8)))
+	  != (golden_data[i / 8] << (8 - (vl % 8))))
+	{
+	  printf ("mask tail diff %lu, tail %d: %d, %d\n", i / 8, vl % 8,
+		  test_data[i / 8], golden_data[i / 8]);
+	  return false;
+	}
+    }
+  return true;
+}
+
+bool
+check_data (int8_t *test_data, int8_t *golden_data, size_t vl)
+{
+  for (size_t i = 0; i < vl; i += 1)
+    {
+      if (test_data[i] != golden_data[i])
+	{
+	  printf ("data diff %lu: %d, %d\n", i, test_data[i], golden_data[i]);
+	  return false;
+	}
+    }
+  return true;
+}
+
+void
+foo1 (vbool1_t a, vbool2_t b, vbool4_t c, vbool2_t *out_b);
+void
+check_foo1 ()
+{
+  INIT_DATA
+
+  size_t vlmax_e8mf8 = __riscv_vsetvlmax_e8mf8 ();
+  vbool1_t a = *(vbool1_t *) dummy_data;
+  vbool2_t b = *(vbool2_t *) golden_data;
+  vbool4_t c = *(vbool4_t *) dummy_data;
+  foo1 (a, b, c, (vbool2_t *) test_data);
+  if (!check_mask (test_data, golden_data, vlmax_e8mf8 * 32))
+    abort ();
+}
+
+void
+foo2 (vbool1_t a, vbool2_t b, vbool4_t c, vbool8_t d, vbool16_t e, vbool32_t f,
+      vbool64_t g, vbool64_t *out_g);
+void
+check_foo2 ()
+{
+  INIT_DATA
+
+  size_t vlmax_e8mf8 = __riscv_vsetvlmax_e8mf8 ();
+  vbool1_t a = *(vbool1_t *) dummy_data;
+  vbool2_t b = *(vbool2_t *) dummy_data;
+  vbool4_t c = *(vbool4_t *) dummy_data;
+  vbool8_t d = *(vbool8_t *) dummy_data;
+  vbool16_t e = *(vbool16_t *) dummy_data;
+  vbool32_t f = *(vbool32_t *) dummy_data;
+  vbool64_t g = *(vbool64_t *) golden_data;
+  foo2 (a, b, c, d, e, f, g, (vbool64_t *) test_data);
+  if (!check_mask (test_data, golden_data, vlmax_e8mf8))
+    abort ();
+}
+
+void
+foo3 (vbool1_t a, vint8m4_t b, vbool2_t c, vbool2_t *out_c);
+void
+check_foo3 ()
+{
+  INIT_DATA
+
+  size_t vlmax_e8mf8 = __riscv_vsetvlmax_e8mf8 ();
+  vbool1_t a = *(vbool1_t *) dummy_data;
+  vint8m4_t b = *(vint8m4_t *) dummy_data;
+  vbool2_t c = *(vbool2_t *) golden_data;
+  foo3 (a, b, c, (vbool2_t *) test_data);
+  if (!check_mask (test_data, golden_data, vlmax_e8mf8 * 32))
+    abort ();
+}
+
+void
+foo4 (vbool1_t a, vint8m4_t b, vbool2_t c, vint8m8_t d, vint8m8_t *out_d);
+void
+check_foo4 ()
+{
+  INIT_DATA
+
+  size_t vlmax_e8mf8 = __riscv_vsetvlmax_e8mf8 ();
+  vbool1_t a = *(vbool1_t *) dummy_data;
+  vint8m4_t b = *(vint8m4_t *) dummy_data;
+  vbool2_t c = *(vbool2_t *) dummy_data;
+  vint8m8_t d = *(vint8m8_t *) golden_data;
+  foo4 (a, b, c, d, (vint8m8_t *) test_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 64))
+    abort ();
+}
+
+void
+foo5 (vbool1_t a, vint8m8_t b, vint8m8_t c, vint8m4_t d, vint8m4_t *out_d);
+void
+check_foo5 ()
+{
+  INIT_DATA
+
+  size_t vlmax_e8mf8 = __riscv_vsetvlmax_e8mf8 ();
+  vbool1_t a = *(vbool1_t *) dummy_data;
+  vint8m8_t b = *(vint8m8_t *) dummy_data;
+  vint8m8_t c = *(vint8m8_t *) dummy_data;
+  vint8m4_t d = *(vint8m4_t *) golden_data;
+  foo5 (a, b, c, d, (vint8m4_t *) test_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 32))
+    abort ();
+}
+
+void
+foo6 (vint8m1_t a, vint8m8_t b, vint8m4_t c, vint8m2_t d, vint8m1_t e,
+      vint8m1_t *out_a, vint8m8_t *out_b, vint8m4_t *out_c, vint8m2_t *out_d,
+      vint8m1_t *out_e);
+void
+check_foo6 ()
+{
+  INIT_DATA
+
+  size_t vlmax_e8mf8 = __riscv_vsetvlmax_e8mf8 ();
+  vint8m1_t a = *(vint8m1_t *) golden_data;
+  vint8m8_t b = *(vint8m8_t *) golden_data;
+  vint8m4_t c = *(vint8m4_t *) golden_data;
+  vint8m2_t d = *(vint8m2_t *) golden_data;
+  vint8m1_t e = *(vint8m1_t *) golden_data;
+  foo6 (a, b, c, d, e, (vint8m1_t *) test_data, (vint8m8_t *) dummy_data,
+	(vint8m4_t *) dummy_data, (vint8m2_t *) dummy_data,
+	(vint8m1_t *) dummy_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 8))
+    abort ();
+
+  foo6 (a, b, c, d, e, (vint8m1_t *) dummy_data, (vint8m8_t *) test_data,
+	(vint8m4_t *) dummy_data, (vint8m2_t *) dummy_data,
+	(vint8m1_t *) dummy_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 64))
+    abort ();
+
+  foo6 (a, b, c, d, e, (vint8m1_t *) dummy_data, (vint8m8_t *) dummy_data,
+	(vint8m4_t *) test_data, (vint8m2_t *) dummy_data,
+	(vint8m1_t *) dummy_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 32))
+    abort ();
+
+  foo6 (a, b, c, d, e, (vint8m1_t *) dummy_data, (vint8m8_t *) dummy_data,
+	(vint8m4_t *) dummy_data, (vint8m2_t *) test_data,
+	(vint8m1_t *) dummy_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 16))
+    abort ();
+
+  foo6 (a, b, c, d, e, (vint8m1_t *) dummy_data, (vint8m8_t *) dummy_data,
+	(vint8m4_t *) dummy_data, (vint8m2_t *) dummy_data,
+	(vint8m1_t *) test_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 8))
+    abort ();
+}
+
+void
+foo7 (vint8m1_t a1, vint8m1_t a2, vint8m1_t a3, vint8m1_t a4, vint8m1_t a5,
+      vint8m1_t a6, vint8m1_t a7, vint8m1_t a8, vint8m1_t a9, vint8m1_t a10,
+      vint8m1_t a11, vint8m1_t a12, vint8m1_t a13, vint8m1_t a14, vint8m1_t a15,
+      vint8m1_t a16, vint8m1_t a17, vint8m1_t *out_a17);
+void
+check_foo7 ()
+{
+  INIT_DATA
+
+  size_t vlmax_e8mf8 = __riscv_vsetvlmax_e8mf8 ();
+  vint8m1_t a1 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a2 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a3 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a4 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a5 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a6 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a7 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a8 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a9 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a10 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a11 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a12 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a13 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a14 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a15 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a16 = *(vint8m1_t *) dummy_data;
+  vint8m1_t a17 = *(vint8m1_t *) golden_data;
+  foo7 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16,
+	a17, (vint8m1_t *) test_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 8))
+    abort ();
+}
+
+void
+foo8 (vint8m8_t a1, vint8m8_t a2, vint8m8_t a3,
+      vint8m8_t *out_a3);
+void
+check_foo8 ()
+{
+  INIT_DATA
+
+  size_t vlmax_e8mf8 = __riscv_vsetvlmax_e8mf8 ();
+  vint8m8_t a1 = *(vint8m8_t *) dummy_data;
+  vint8m8_t a2 = *(vint8m8_t *) dummy_data;
+  vint8m8_t a3 = *(vint8m8_t *) golden_data;
+
+  foo8 (a1, a2, a3, (vint8m8_t *) test_data);
+  if (!check_data (test_data, golden_data, vlmax_e8mf8 * 64))
+    abort ();
+}
+
+int
+main ()
+{
+  check_foo1 ();
+  check_foo2 ();
+  check_foo3 ();
+  check_foo4 ();
+  check_foo5 ();
+  check_foo6 ();
+  check_foo7 ();
+  check_foo8 ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-3.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-3.c
new file mode 100644
index 00000000000..435d61c2c21
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-3.c
@@ -0,0 +1,116 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "riscv_vector.h"
+
+/* Test args order.  */
+
+/*
+** foo1:
+**   ...
+**   vsm\.v\tv8,0\(a0\)
+**   ...
+*/
+void
+foo1 (vbool1_t a, vbool2_t b, vbool4_t c, vbool2_t *out_b)
+{
+  *out_b = b;
+}
+
+/*
+** foo2:
+**   ...
+**   vsm\.v\tv13,0\(a0\)
+**   ...
+*/
+void
+foo2 (vbool1_t a, vbool2_t b, vbool4_t c, vbool8_t d, vbool16_t e, vbool32_t f,
+      vbool64_t g, vbool64_t *out_g)
+{
+  *out_g = g;
+}
+
+/*
+** foo3:
+**   ...
+**   vsm\.v\tv12,0\(a0\)
+**   ...
+*/
+void
+foo3 (vbool1_t a, vint8m4_t b, vbool2_t c, vbool2_t *out_c)
+{
+  *out_c = c;
+}
+
+/*
+** foo4:
+**   vs8r\.v\tv16,0\(a0\)
+**   ...
+*/
+void
+foo4 (vbool1_t a, vint8m4_t b, vbool2_t c, vint8m8_t d, vint8m8_t *out_d)
+{
+  *out_d = d;
+}
+
+/*
+** foo5:
+**   vl4re8\.v\tv[0-9]+,0\(a0\)
+**   ...
+**   vs4r\.v\tv[0-9]+,0\(a1\)
+**   ...
+*/
+void
+foo5 (vbool1_t a, vint8m8_t b, vint8m8_t c, vint8m4_t d, vint8m4_t *out_d)
+{
+  *out_d = d;
+}
+
+/*
+** foo6:
+**   vs1r\.v\tv8,0\(a0\)
+**   vs8r\.v\tv16,0\(a1\)
+**   vs4r\.v\tv12,0\(a2\)
+**   vs2r\.v\tv10,0\(a3\)
+**   vs1r\.v\tv9,0\(a4\)
+**   ...
+*/
+void
+foo6 (vint8m1_t a, vint8m8_t b, vint8m4_t c, vint8m2_t d, vint8m1_t e,
+      vint8m1_t *out_a, vint8m8_t *out_b, vint8m4_t *out_c, vint8m2_t *out_d,
+      vint8m1_t *out_e)
+{
+  *out_a = a;
+  *out_b = b;
+  *out_c = c;
+  *out_d = d;
+  *out_e = e;
+}
+
+/*
+** foo7:
+**   vl1re8\.v\tv\d+,0\(a0\)
+**   vs1r\.v\tv\d+,0\(a1\)
+**   ...
+*/
+void
+foo7 (vint8m1_t a1, vint8m1_t a2, vint8m1_t a3, vint8m1_t a4, vint8m1_t a5,
+      vint8m1_t a6, vint8m1_t a7, vint8m1_t a8, vint8m1_t a9, vint8m1_t a10,
+      vint8m1_t a11, vint8m1_t a12, vint8m1_t a13, vint8m1_t a14, vint8m1_t a15,
+      vint8m1_t a16, vint8m1_t a17, vint8m1_t *out_a17)
+{
+  *out_a17 = a17;
+}
+
+/*
+** foo8:
+**   vl8re8\.v\tv\d+,0\(a0\)
+**   vs8r\.v\tv\d+,0\(a1\)
+**   ...
+*/
+void
+foo8 (vint8m8_t a1, vint8m8_t a2, vint8m8_t a3, vint8m8_t *out_a3)
+{
+  *out_a3 = a3;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-4-run.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-4-run.c
new file mode 100644
index 00000000000..f13ab7a8ade
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-4-run.c
@@ -0,0 +1,145 @@ 
+/* { dg-do run } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-additional-sources abi-call-args-4.c } */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "riscv_vector.h"
+
+vint64m8_t
+foo1 (vint8m1_t a1, vint8m1_t a2, vint16m2_t b1, vint32m4_t c1, vint64m8_t d1,
+      size_t vl);
+vint64m8_t
+foo2 (vint8m1_t a1, vint16m2_t b1, vint8m1_t a2, vint32m4_t c1, vint64m8_t d1,
+      size_t vl);
+vint64m8_t
+foo3 (vint8m1_t a1, vint16m2_t b1, vint32m4_t c1, vint8m1_t a2, vint64m8_t d1,
+      size_t vl);
+vint64m8_t
+foo4 (vint8m1_t a1, vint16m2_t b1, vint32m4_t c1, vint64m8_t d1, vint8m1_t a2,
+      size_t vl);
+
+vint8m1x8_t
+foo5 (vint8m8_t a1, vint8m1x8_t a2);
+
+int
+main ()
+{
+  size_t vlmax_e8m1 = __riscv_vsetvlmax_e8m1 ();
+  int8_t a1[vlmax_e8m1], a2[vlmax_e8m1];
+  int16_t b1[vlmax_e8m1], b2[vlmax_e8m1];
+  int32_t c1[vlmax_e8m1], c2[vlmax_e8m1];
+  int64_t d1[vlmax_e8m1], d2[vlmax_e8m1];
+  memset (a1, 0, vlmax_e8m1 * sizeof (int8_t));
+  memset (a2, 0, vlmax_e8m1 * sizeof (int8_t));
+  memset (b1, 0, vlmax_e8m1 * sizeof (int16_t));
+  memset (b2, 0, vlmax_e8m1 * sizeof (int16_t));
+  memset (c1, 0, vlmax_e8m1 * sizeof (int32_t));
+  memset (c2, 0, vlmax_e8m1 * sizeof (int32_t));
+  memset (d1, 0, vlmax_e8m1 * sizeof (int64_t));
+  memset (d2, 0, vlmax_e8m1 * sizeof (int64_t));
+
+  for (size_t i = 0; i < vlmax_e8m1; i++)
+    {
+      a1[i] = 67 * i;
+      a2[i] = 83 * i;
+      b1[i] = 132 * i;
+      c1[i] = 1928 * i;
+      d1[i] = 23495 * i;
+    }
+
+  for (size_t i = 0; i < vlmax_e8m1; i++)
+    {
+      b2[i] = a1[i] + a2[i];
+    }
+  for (size_t i = 0; i < vlmax_e8m1; i++)
+    {
+      c2[i] = b1[i] - b2[i];
+    }
+
+  for (size_t i = 0; i < vlmax_e8m1; i++)
+    {
+      d2[i] = c1[i] * c2[i];
+      d2[i] = d2[i] & d1[i];
+    }
+  int64_t golden = 0;
+  for (size_t i = 0; i < vlmax_e8m1; i++)
+    {
+      golden += d2[i];
+    }
+
+  int64_t test;
+
+  vint64m8_t res1
+    = foo1 (*(vint8m1_t *) a1, *(vint8m1_t *) a2, *(vint16m2_t *) b1,
+	    *(vint32m4_t *) c1, *(vint64m8_t *) d1, vlmax_e8m1);
+  test = __riscv_vmv_x_s_i64m1_i64 (
+    __riscv_vredsum_vs_i64m8_i64m1 (res1, __riscv_vmv_v_x_i64m1 (0, vlmax_e8m1),
+				    vlmax_e8m1));
+
+  if (test != golden)
+    {
+      printf ("foo1: %ld, %ld\n", test, golden);
+      abort ();
+    }
+
+  vint64m8_t res2
+    = foo2 (*(vint8m1_t *) a1, *(vint16m2_t *) b1, *(vint8m1_t *) a2,
+	    *(vint32m4_t *) c1, *(vint64m8_t *) d1, vlmax_e8m1);
+  test = __riscv_vmv_x_s_i64m1_i64 (
+    __riscv_vredsum_vs_i64m8_i64m1 (res2, __riscv_vmv_v_x_i64m1 (0, vlmax_e8m1),
+				    vlmax_e8m1));
+
+  if (test != golden)
+    {
+      printf ("foo2: %ld, %ld\n", test, golden);
+      abort ();
+    }
+
+  vint64m8_t res3
+    = foo3 (*(vint8m1_t *) a1, *(vint16m2_t *) b1, *(vint32m4_t *) c1,
+	    *(vint8m1_t *) a2, *(vint64m8_t *) d1, vlmax_e8m1);
+  test = __riscv_vmv_x_s_i64m1_i64 (
+    __riscv_vredsum_vs_i64m8_i64m1 (res3, __riscv_vmv_v_x_i64m1 (0, vlmax_e8m1),
+				    vlmax_e8m1));
+  if (test != golden)
+    {
+      printf ("foo3: %ld, %ld\n", test, golden);
+      abort ();
+    }
+
+  vint64m8_t res4
+    = foo4 (*(vint8m1_t *) a1, *(vint16m2_t *) b1, *(vint32m4_t *) c1,
+	    *(vint64m8_t *) d1, *(vint8m1_t *) a2, vlmax_e8m1);
+  test = __riscv_vmv_x_s_i64m1_i64 (
+    __riscv_vredsum_vs_i64m8_i64m1 (res4, __riscv_vmv_v_x_i64m1 (0, vlmax_e8m1),
+				    vlmax_e8m1));
+  if (test != golden)
+    {
+      printf ("foo4: %ld, %ld\n", test, golden);
+      abort ();
+    }
+
+  int8_t t1[vlmax_e8m1 * 8];
+  int8_t t2[vlmax_e8m1 * 8];
+  for (size_t i = 0; i < vlmax_e8m1 * 8; i++)
+    {
+      t1[i] = 67 * i;
+      t2[i] = 83 * i;
+    }
+  vint8m1x8_t res5 = foo5 (*(vint8m8_t *) t1, *(vint8m1x8_t *) t2);
+  int8_t test_arr[vlmax_e8m1 * 8];
+  memset (test_arr, 0, vlmax_e8m1 * 8 * sizeof (int8_t));
+  *(vint8m1x8_t *) test_arr = res5;
+  for (size_t i = 0; i < vlmax_e8m1 * 8; i += 1)
+    if (t2[i] != test_arr[i])
+      {
+	printf ("foo5 %d: %ld, %ld\n", i, test_arr[i], t2[i]);
+	abort ();
+      }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-4.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-4.c
new file mode 100644
index 00000000000..b1d99dff979
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-args-4.c
@@ -0,0 +1,111 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "riscv_vector.h"
+
+/*
+** foo1:
+**   ...
+**   vwadd\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vwsub\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vwmul\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vand\.vv\tv8,v\d+,v\d+
+**   ...
+*/
+vint64m8_t
+foo1 (vint8m1_t a1, vint8m1_t a2, vint16m2_t b1, vint32m4_t c1, vint64m8_t d1,
+      size_t vl)
+{
+  vint16m2_t b2 = __riscv_vwadd_vv_i16m2 (a1, a2, vl);
+  vint32m4_t c2 = __riscv_vwsub_vv_i32m4 (b1, b2, vl);
+  vint64m8_t d2 = __riscv_vwmul_vv_i64m8 (c1, c2, vl);
+  return __riscv_vand_vv_i64m8 (d1, d2, vl);
+}
+
+/*
+** foo2:
+**   ...
+**   vwadd\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vwsub\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vwmul\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vand\.vv\tv8,v\d+,v\d+
+**   ...
+*/
+vint64m8_t
+foo2 (vint8m1_t a1, vint16m2_t b1, vint8m1_t a2, vint32m4_t c1, vint64m8_t d1,
+      size_t vl)
+{
+  vint16m2_t b2 = __riscv_vwadd_vv_i16m2 (a1, a2, vl);
+  vint32m4_t c2 = __riscv_vwsub_vv_i32m4 (b1, b2, vl);
+  vint64m8_t d2 = __riscv_vwmul_vv_i64m8 (c1, c2, vl);
+  return __riscv_vand_vv_i64m8 (d1, d2, vl);
+}
+
+/*
+** foo3:
+**   ...
+**   vwadd\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vwsub\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vwmul\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vand\.vv\tv8,v\d+,v\d+
+**   ...
+*/
+vint64m8_t
+foo3 (vint8m1_t a1, vint16m2_t b1, vint32m4_t c1, vint8m1_t a2, vint64m8_t d1,
+      size_t vl)
+{
+  vint16m2_t b2 = __riscv_vwadd_vv_i16m2 (a1, a2, vl);
+  vint32m4_t c2 = __riscv_vwsub_vv_i32m4 (b1, b2, vl);
+  vint64m8_t d2 = __riscv_vwmul_vv_i64m8 (c1, c2, vl);
+  return __riscv_vand_vv_i64m8 (d1, d2, vl);
+}
+
+/*
+** foo4:
+**   ...
+**   vwadd\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vwsub\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vwmul\.vv\tv\d+,v\d+,v\d+
+**   ...
+**   vand\.vv\tv8,v\d+,v\d+
+**   ...
+*/
+vint64m8_t
+foo4 (vint8m1_t a1, vint16m2_t b1, vint32m4_t c1, vint64m8_t d1, vint8m1_t a2,
+      size_t vl)
+{
+  vint16m2_t b2 = __riscv_vwadd_vv_i16m2 (a1, a2, vl);
+  vint32m4_t c2 = __riscv_vwsub_vv_i32m4 (b1, b2, vl);
+  vint64m8_t d2 = __riscv_vwmul_vv_i64m8 (c1, c2, vl);
+  return __riscv_vand_vv_i64m8 (d1, d2, vl);
+}
+
+/*
+** foo5:
+**   vmv1r\.v\tv8,v16
+**   vmv1r\.v\tv9,v17
+**   vmv1r\.v\tv10,v18
+**   vmv1r\.v\tv11,v19
+**   vmv1r\.v\tv12,v20
+**   vmv1r\.v\tv13,v21
+**   vmv1r\.v\tv14,v22
+**   vmv1r\.v\tv15,v23
+**   ...
+*/
+vint8m1x8_t
+foo5 (vint8m8_t a, vint8m1x8_t b)
+{
+  return b;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-error-1.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-error-1.c
new file mode 100644
index 00000000000..fce548303ca
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-error-1.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "--param=riscv-vector-abi -Wno-psabi -Wno-implicit-function-declaration" } */
+
+#include "riscv_vector.h"
+
+int
+foo (int8_t *in)
+{
+  vint8m1_t a = *(vint8m1_t *)in;
+  bar (a); /* { dg-error "RVV type 'vint8m1_t' cannot be passed to an unprototyped function" } */
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-return-run.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-return-run.c
new file mode 100644
index 00000000000..7802b2ff667
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-return-run.c
@@ -0,0 +1,127 @@ 
+/* { dg-do run } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-additional-sources abi-call-return.c } */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "riscv_vector.h"
+
+#define FOO_(TYPE) TYPE foo_##TYPE (TYPE *out);
+
+FOO_ (vbool1_t)
+FOO_ (vbool2_t)
+FOO_ (vbool4_t)
+FOO_ (vbool8_t)
+FOO_ (vbool16_t)
+FOO_ (vbool32_t)
+FOO_ (vbool64_t)
+FOO_ (vint8mf8_t)
+FOO_ (vint8mf4_t)
+FOO_ (vint8mf2_t)
+FOO_ (vint8m1_t)
+FOO_ (vint8m2_t)
+FOO_ (vint8m4_t)
+FOO_ (vint8m8_t)
+FOO_ (vint8m1x5_t)
+FOO_ (vint8m1x8_t)
+FOO_ (vint8m2x3_t)
+FOO_ (vint8m2x4_t)
+FOO_ (vint8m4x2_t)
+
+bool
+check_mask (int8_t *test_data, int8_t *golden_data, size_t vl)
+{
+  size_t i = 0;
+  for (; i + 8 <= vl; i += 8)
+    {
+      if (test_data[i / 8] != golden_data[i / 8])
+	{
+	  printf ("mask diff %lu: %d, %d\n", i / 8, test_data[i / 8],
+		  golden_data[i / 8]);
+	  return false;
+	}
+    }
+  if (vl % 8 != 0)
+    {
+      if ((test_data[i / 8] << (8 - (vl % 8)))
+	  != (golden_data[i / 8] << (8 - (vl % 8))))
+	{
+	  printf ("mask tail diff %lu, tail %d: %d, %d\n", i / 8, vl % 8,
+		  test_data[i / 8], golden_data[i / 8]);
+	  return false;
+	}
+    }
+  return true;
+}
+
+bool
+check_data (int8_t *test_data, int8_t *golden_data, size_t vl)
+{
+  for (size_t i = 0; i < vl; i += 1)
+    {
+      if (test_data[i] != golden_data[i])
+	{
+	  printf ("data diff %lu: %d, %d\n", i, test_data[i], golden_data[i]);
+	  return false;
+	}
+    }
+  return true;
+}
+
+#define INIT_DATA                                                              \
+  size_t vlmax_e8m8 = __riscv_vsetvlmax_e8m8 ();                               \
+  int8_t golden_data[vlmax_e8m8];                                              \
+  memset (golden_data, 0, vlmax_e8m8 * sizeof (int8_t));                       \
+  int8_t test_data[vlmax_e8m8];                                                \
+  memset (test_data, 0, vlmax_e8m8 * sizeof (int8_t));                         \
+  for (size_t i = 0; i < vlmax_e8m8; i += 1)                                   \
+    golden_data[i] = vlmax_e8m8 - 1;
+
+#define FOO_MASK(TYPE, VL)                                                     \
+  {                                                                            \
+    INIT_DATA                                                                  \
+    for (size_t i = 0; i < vlmax_e8m8; i += 1)                                 \
+      test_data[i] = 0;                                                        \
+    TYPE val = foo_##TYPE ((TYPE *) golden_data);                              \
+    *(TYPE *) test_data = val;                                                 \
+    if (!check_mask (test_data, golden_data, VL))                              \
+      abort ();                                                                \
+  }
+
+#define FOO_DATA(TYPE, VL)                                                     \
+  {                                                                            \
+    INIT_DATA                                                                  \
+    for (size_t i = 0; i < vlmax_e8m8; i += 1)                                 \
+      test_data[i] = 0;                                                        \
+    TYPE val = foo_##TYPE ((TYPE *) golden_data);                              \
+    *(TYPE *) test_data = val;                                                 \
+    if (!check_data (test_data, golden_data, VL))                              \
+      abort ();                                                                \
+  }
+
+int
+main ()
+{
+  size_t vlmax = __riscv_vsetvlmax_e8mf8 ();
+  FOO_MASK (vbool1_t, vlmax * 64)
+  FOO_MASK (vbool2_t, vlmax * 32)
+  FOO_MASK (vbool4_t, vlmax * 16)
+  FOO_MASK (vbool8_t, vlmax * 8)
+  FOO_MASK (vbool16_t, vlmax * 4)
+  FOO_MASK (vbool32_t, vlmax * 2)
+  FOO_MASK (vbool64_t, vlmax)
+  FOO_DATA (vint8mf8_t, vlmax)
+  FOO_DATA (vint8mf4_t, vlmax * 2)
+  FOO_DATA (vint8mf2_t, vlmax * 4)
+  FOO_DATA (vint8m1_t, vlmax * 8)
+  FOO_DATA (vint8m2_t, vlmax * 16)
+  FOO_DATA (vint8m4_t, vlmax * 32)
+  FOO_DATA (vint8m8_t, vlmax * 64)
+  FOO_DATA (vint8m1x5_t, vlmax * 8 * 5)
+  FOO_DATA (vint8m1x8_t, vlmax * 8 * 8)
+  FOO_DATA (vint8m2x3_t, vlmax * 16 * 3)
+  FOO_DATA (vint8m2x4_t, vlmax * 16 * 4)
+  FOO_DATA (vint8m4x2_t, vlmax * 32 * 2)
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-return.c b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-return.c
new file mode 100644
index 00000000000..0e0f3fce299
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-return.c
@@ -0,0 +1,197 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O1 --param=riscv-vector-abi -Wno-psabi" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "riscv_vector.h"
+
+#define FOO_(TYPE)                                                             \
+  TYPE foo_##TYPE (TYPE *out)                                                  \
+  {                                                                            \
+    return *out;                                                               \
+  }
+
+/* Test the first vector mask type argument */
+
+/*
+** foo_vbool1_t:
+**   ...
+**   vlm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool1_t)
+
+/*
+** foo_vbool2_t:
+**   ...
+**   vlm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool2_t)
+
+/*
+** foo_vbool4_t:
+**   ...
+**   vlm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool4_t)
+
+/*
+** foo_vbool8_t:
+**   ...
+**   vlm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool8_t)
+
+/*
+** foo_vbool16_t:
+**   ...
+**   vlm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool16_t)
+
+/*
+** foo_vbool32_t:
+**   ...
+**   vlm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool32_t)
+
+/*
+** foo_vbool64_t:
+**   ...
+**   vlm\.v\tv0,0\(a0\)
+**   ...
+*/
+FOO_ (vbool64_t)
+
+/* Test the first vector data type argument */
+
+/*
+** foo_vint8mf8_t:
+**   ...
+**   vle8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8mf8_t)
+
+/*
+** foo_vint8mf4_t:
+**   ...
+**   vle8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8mf4_t)
+
+/*
+** foo_vint8mf2_t:
+**   ...
+**   vle8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8mf2_t)
+
+/*
+** foo_vint8m1_t:
+**   vl1re8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8m1_t)
+
+/*
+** foo_vint8m2_t:
+**   vl2re8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8m2_t)
+
+/*
+** foo_vint8m4_t:
+**   vl4re8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8m4_t)
+
+/*
+** foo_vint8m8_t:
+**   vl8re8\.v\tv8,0\(a0\)
+**   ...
+*/
+FOO_ (vint8m8_t)
+
+/*
+** foo_vint8m1x5_t:
+**   ...
+**   vl1re8\.v\tv8,0\(a0\)
+**   ...
+**   vl1re8\.v\tv9,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv10,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv11,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv12,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m1x5_t)
+
+/*
+** foo_vint8m1x8_t:
+**   ...
+**   vl1re8\.v\tv8,0\(a0\)
+**   ...
+**   vl1re8\.v\tv9,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv10,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv11,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv12,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv13,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv14,0\(a\d+\)
+**   ...
+**   vl1re8\.v\tv15,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m1x8_t)
+
+/*
+** foo_vint8m2x3_t:
+**   ...
+**   vl2re8\.v\tv8,0\(a0\)
+**   ...
+**   vl2re8\.v\tv10,0\(a\d+\)
+**   ...
+**   vl2re8\.v\tv12,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m2x3_t)
+
+/*
+** foo_vint8m2x4_t:
+**   ...
+**   vl2re8\.v\tv8,0\(a0\)
+**   ...
+**   vl2re8\.v\tv10,0\(a\d+\)
+**   ...
+**   vl2re8\.v\tv12,0\(a\d+\)
+**   ...
+**   vl2re8\.v\tv14,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m2x4_t)
+
+/*
+** foo_vint8m4x2_t:
+**   ...
+**   vl4re8\.v\tv8,0\(a0\)
+**   ...
+**   vl4re8\.v\tv12,0\(a\d+\)
+**   ...
+*/
+FOO_ (vint8m4x2_t)