Skip to content

Commit 7c71d3f

Browse files
lhtinChen Yixuan
authored and
Chen Yixuan
committed
RISC-V: Part-1: Select suitable vector registers for vector type args and returns
I post the vector register calling convention rules from in the proposal[1] directly here: 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] riscv-non-isa/riscv-elf-psabi-doc#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.
1 parent 1fad284 commit 7c71d3f

16 files changed

+1610
-17
lines changed

gcc/config/riscv/riscv-protos.h

+1
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ enum avl_type
366366
void init_builtins (void);
367367
const char *mangle_builtin_type (const_tree);
368368
tree lookup_vector_type_attribute (const_tree);
369+
bool builtin_type_p (const_tree);
369370
#ifdef GCC_TARGET_H
370371
bool verify_type_context (location_t, type_context_kind, const_tree, bool);
371372
bool expand_vec_perm_const (machine_mode, machine_mode, rtx, rtx, rtx,

gcc/config/riscv/riscv-vector-builtins.cc

+10
Original file line numberDiff line numberDiff line change
@@ -3992,6 +3992,16 @@ mangle_builtin_type (const_tree type)
39923992
return NULL;
39933993
}
39943994

3995+
/* Return true if TYPE is a built-in RVV type defined by the ABI. */
3996+
bool
3997+
builtin_type_p (const_tree type)
3998+
{
3999+
if (!type)
4000+
return false;
4001+
4002+
return lookup_vector_type_attribute (type);
4003+
}
4004+
39954005
/* Initialize all compiler built-ins related to RVV that should be
39964006
defined at start-up. */
39974007
void

gcc/config/riscv/riscv.cc

+217-17
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,18 @@ struct riscv_arg_info {
202202

203203
/* The offset of the first register used, provided num_fprs is nonzero. */
204204
unsigned int fpr_offset;
205+
206+
/* The number of vector registers allocated to this argument. */
207+
unsigned int num_vrs;
208+
209+
/* The offset of the first register used, provided num_vrs is nonzero. */
210+
unsigned int vr_offset;
211+
212+
/* The number of mask registers allocated to this argument. */
213+
unsigned int num_mrs;
214+
215+
/* The offset of the first register used, provided num_mrs is nonzero. */
216+
unsigned int mr_offset;
205217
};
206218

207219
/* One stage in a constant building sequence. These sequences have
@@ -4413,6 +4425,11 @@ riscv_init_cumulative_args (CUMULATIVE_ARGS *cum,
44134425
{
44144426
memset (cum, 0, sizeof (*cum));
44154427

4428+
if (fntype)
4429+
cum->variant_cc = (riscv_cc) fntype_abi (fntype).id ();
4430+
else
4431+
cum->variant_cc = RISCV_CC_BASE;
4432+
44164433
if (fndecl)
44174434
{
44184435
const tree_function_decl &fn
@@ -4423,12 +4440,105 @@ riscv_init_cumulative_args (CUMULATIVE_ARGS *cum,
44234440
}
44244441
}
44254442

4426-
/* Fill INFO with information about a single argument, and return an
4427-
RTL pattern to pass or return the argument. CUM is the cumulative
4428-
state for earlier arguments. MODE is the mode of this argument and
4429-
TYPE is its type (if known). NAMED is true if this is a named
4430-
(fixed) argument rather than a variable one. RETURN_P is true if
4431-
returning the argument, or false if passing the argument. */
4443+
/* Return true if TYPE is a vector type that can be passed in vector registers.
4444+
*/
4445+
4446+
static bool
4447+
riscv_vector_type_p (const_tree type)
4448+
{
4449+
/* Currently, only builtin scalabler vector type is allowed, in the future,
4450+
more vector types may be allowed, such as GNU vector type, etc. */
4451+
return riscv_vector::builtin_type_p (type);
4452+
}
4453+
4454+
static unsigned int
4455+
riscv_hard_regno_nregs (unsigned int regno, machine_mode mode);
4456+
4457+
/* Subroutine of riscv_get_arg_info. */
4458+
4459+
static rtx
4460+
riscv_get_vector_arg (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
4461+
machine_mode mode, bool return_p)
4462+
{
4463+
gcc_assert (riscv_v_ext_mode_p (mode));
4464+
4465+
info->mr_offset = cum->num_mrs;
4466+
if (GET_MODE_CLASS (mode) == MODE_VECTOR_BOOL)
4467+
{
4468+
/* For scalable mask return value. */
4469+
if (return_p)
4470+
return gen_rtx_REG (mode, V_REG_FIRST);
4471+
4472+
/* For the first scalable mask argument. */
4473+
if (info->mr_offset < MAX_ARGS_IN_MASK_REGISTERS)
4474+
{
4475+
info->num_mrs = 1;
4476+
return gen_rtx_REG (mode, V_REG_FIRST);
4477+
}
4478+
else
4479+
{
4480+
/* Rest scalable mask arguments are treated as scalable data
4481+
arguments. */
4482+
}
4483+
}
4484+
4485+
/* The number and alignment of vector registers need for this scalable vector
4486+
argument. When the mode size is less than a full vector, we use 1 vector
4487+
register to pass. Just call TARGET_HARD_REGNO_NREGS for the number
4488+
information. */
4489+
int nregs = riscv_hard_regno_nregs (V_ARG_FIRST, mode);
4490+
int LMUL = riscv_v_ext_tuple_mode_p (mode)
4491+
? nregs / riscv_vector::get_nf (mode)
4492+
: nregs;
4493+
int arg_reg_start = V_ARG_FIRST - V_REG_FIRST;
4494+
int arg_reg_end = V_ARG_LAST - V_REG_FIRST;
4495+
int aligned_reg_start = ROUND_UP (arg_reg_start, LMUL);
4496+
4497+
/* For scalable data and scalable tuple return value. */
4498+
if (return_p)
4499+
return gen_rtx_REG (mode, aligned_reg_start + V_REG_FIRST);
4500+
4501+
/* Iterate through the USED_VRS array to find vector register groups that have
4502+
not been allocated and the first register is aligned with LMUL. */
4503+
for (int i = aligned_reg_start; i + nregs - 1 <= arg_reg_end; i += LMUL)
4504+
{
4505+
/* The index in USED_VRS array. */
4506+
int idx = i - arg_reg_start;
4507+
/* Find the first register unused. */
4508+
if (!cum->used_vrs[idx])
4509+
{
4510+
bool find_set = true;
4511+
/* Ensure there are NREGS continuous unused registers. */
4512+
for (int j = 1; j < nregs; j++)
4513+
if (cum->used_vrs[idx + j])
4514+
{
4515+
find_set = false;
4516+
/* Update I to the last aligned register which
4517+
cannot be used and the next iteration will add
4518+
LMUL step to I. */
4519+
i += (j / LMUL) * LMUL;
4520+
break;
4521+
}
4522+
4523+
if (find_set)
4524+
{
4525+
info->num_vrs = nregs;
4526+
info->vr_offset = idx;
4527+
return gen_rtx_REG (mode, i + V_REG_FIRST);
4528+
}
4529+
}
4530+
}
4531+
4532+
return NULL_RTX;
4533+
}
4534+
4535+
/* Fill INFO with information about a single argument, and return an RTL
4536+
pattern to pass or return the argument. Return NULL_RTX if argument cannot
4537+
pass or return in registers, then the argument may be passed by reference or
4538+
through the stack or . CUM is the cumulative state for earlier arguments.
4539+
MODE is the mode of this argument and TYPE is its type (if known). NAMED is
4540+
true if this is a named (fixed) argument rather than a variable one. RETURN_P
4541+
is true if returning the argument, or false if passing the argument. */
44324542

44334543
static rtx
44344544
riscv_get_arg_info (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
@@ -4450,11 +4560,9 @@ riscv_get_arg_info (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
44504560
riscv_pass_in_vector_p (type);
44514561
}
44524562

4453-
/* All current vector arguments and return values are passed through the
4454-
function stack. Ideally, we should either warn the user not to use an RVV
4455-
vector type as function argument or support a calling convention
4456-
with better performance. */
4457-
if (riscv_v_ext_mode_p (mode))
4563+
/* When disable vector_abi or scalable vector argument is anonymous, this
4564+
argument is passed by reference. */
4565+
if (riscv_v_ext_mode_p (mode) && (!riscv_vector_abi || !named))
44584566
return NULL_RTX;
44594567

44604568
if (named)
@@ -4518,6 +4626,10 @@ riscv_get_arg_info (struct riscv_arg_info *info, const CUMULATIVE_ARGS *cum,
45184626
gregno, TYPE_MODE (fields[1].type),
45194627
fields[1].offset);
45204628
}
4629+
4630+
/* For scalable vector argument. */
4631+
if (riscv_vector_type_p (type) && riscv_v_ext_mode_p (mode))
4632+
return riscv_get_vector_arg (info, cum, mode, return_p);
45214633
}
45224634

45234635
/* Work out the size of the argument. */
@@ -4564,12 +4676,28 @@ riscv_function_arg_advance (cumulative_args_t cum_v,
45644676

45654677
riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false);
45664678

4679+
/* Set the corresponding register in USED_VRS to used status. */
4680+
for (unsigned int i = 0; i < info.num_vrs; i++)
4681+
{
4682+
gcc_assert (!cum->used_vrs[info.vr_offset + i]);
4683+
cum->used_vrs[info.vr_offset + i] = true;
4684+
}
4685+
4686+
if ((info.num_vrs > 0 || info.num_mrs > 0) && cum->variant_cc != RISCV_CC_V)
4687+
{
4688+
error ("RVV type %qT cannot be passed to an unprototyped function",
4689+
arg.type);
4690+
/* Avoid repeating the message */
4691+
cum->variant_cc = RISCV_CC_V;
4692+
}
4693+
45674694
/* Advance the register count. This has the effect of setting
45684695
num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned
45694696
argument required us to skip the final GPR and pass the whole
45704697
argument on the stack. */
45714698
cum->num_fprs = info.fpr_offset + info.num_fprs;
45724699
cum->num_gprs = info.gpr_offset + info.num_gprs;
4700+
cum->num_mrs = info.mr_offset + info.num_mrs;
45734701
}
45744702

45754703
/* Implement TARGET_ARG_PARTIAL_BYTES. */
@@ -4631,20 +4759,23 @@ riscv_pass_by_reference (cumulative_args_t cum_v, const function_arg_info &arg)
46314759
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
46324760

46334761
/* ??? std_gimplify_va_arg_expr passes NULL for cum. Fortunately, we
4634-
never pass variadic arguments in floating-point registers, so we can
4635-
avoid the call to riscv_get_arg_info in this case. */
4762+
never pass variadic arguments in floating-point and vector registers,
4763+
so we can avoid the call to riscv_get_arg_info in this case. */
46364764
if (cum != NULL)
46374765
{
46384766
/* Don't pass by reference if we can use a floating-point register. */
46394767
riscv_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false);
46404768
if (info.num_fprs)
46414769
return false;
4770+
4771+
/* Don't pass by reference if we can use vector register groups. */
4772+
if (info.num_vrs > 0 || info.num_mrs > 0)
4773+
return false;
46424774
}
46434775

4644-
/* All current vector arguments and return values are passed through the
4645-
function stack. Ideally, we should either warn the user not to use an RVV
4646-
vector type as function argument or support a calling convention
4647-
with better performance. */
4776+
/* When vector abi disabled(without --param=riscv-vector-abi option) or
4777+
scalable vector argument is anonymous or cannot be passed through vector
4778+
registers, this argument is passed by reference. */
46484779
if (riscv_v_ext_mode_p (arg.mode))
46494780
return true;
46504781

@@ -4702,6 +4833,73 @@ riscv_setup_incoming_varargs (cumulative_args_t cum,
47024833
cfun->machine->varargs_size = gp_saved * UNITS_PER_WORD;
47034834
}
47044835

4836+
/* Return the descriptor of the Standard Vector Calling Convention Variant. */
4837+
4838+
static const predefined_function_abi &
4839+
riscv_v_abi ()
4840+
{
4841+
predefined_function_abi &v_abi = function_abis[RISCV_CC_V];
4842+
if (!v_abi.initialized_p ())
4843+
{
4844+
HARD_REG_SET full_reg_clobbers
4845+
= default_function_abi.full_reg_clobbers ();
4846+
/* Callee-saved vector registers: v1-v7, v24-v31. */
4847+
for (int regno = V_REG_FIRST + 1; regno <= V_REG_FIRST + 7; regno += 1)
4848+
CLEAR_HARD_REG_BIT (full_reg_clobbers, regno);
4849+
for (int regno = V_REG_FIRST + 24; regno <= V_REG_FIRST + 31; regno += 1)
4850+
CLEAR_HARD_REG_BIT (full_reg_clobbers, regno);
4851+
v_abi.initialize (RISCV_CC_V, full_reg_clobbers);
4852+
}
4853+
return v_abi;
4854+
}
4855+
4856+
/* Return true if a function with type FNTYPE returns its value in
4857+
RISC-V V registers. */
4858+
4859+
static bool
4860+
riscv_return_value_is_vector_type_p (const_tree fntype)
4861+
{
4862+
tree return_type = TREE_TYPE (fntype);
4863+
4864+
return riscv_vector_type_p (return_type);
4865+
}
4866+
4867+
/* Return true if a function with type FNTYPE takes arguments in
4868+
RISC-V V registers. */
4869+
4870+
static bool
4871+
riscv_arguments_is_vector_type_p (const_tree fntype)
4872+
{
4873+
for (tree chain = TYPE_ARG_TYPES (fntype); chain && chain != void_list_node;
4874+
chain = TREE_CHAIN (chain))
4875+
{
4876+
tree arg_type = TREE_VALUE (chain);
4877+
if (riscv_vector_type_p (arg_type))
4878+
return true;
4879+
}
4880+
4881+
return false;
4882+
}
4883+
4884+
/* Implement TARGET_FNTYPE_ABI. */
4885+
4886+
static const predefined_function_abi &
4887+
riscv_fntype_abi (const_tree fntype)
4888+
{
4889+
/* Implementing an experimental vector calling convention, the proposal
4890+
can be viewed at the bellow link:
4891+
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/389
4892+
4893+
You can enable this feature via the `--param=riscv-vector-abi` compiler
4894+
option. */
4895+
if (riscv_vector_abi
4896+
&& (riscv_return_value_is_vector_type_p (fntype)
4897+
|| riscv_arguments_is_vector_type_p (fntype)))
4898+
return riscv_v_abi ();
4899+
4900+
return default_function_abi;
4901+
}
4902+
47054903
/* Handle an attribute requiring a FUNCTION_DECL;
47064904
arguments as in struct attribute_spec.handler. */
47074905
static tree
@@ -9159,6 +9357,8 @@ riscv_vectorize_create_costs (vec_info *vinfo, bool costing_for_scalar)
91599357
#define TARGET_FUNCTION_ARG_ADVANCE riscv_function_arg_advance
91609358
#undef TARGET_FUNCTION_ARG_BOUNDARY
91619359
#define TARGET_FUNCTION_ARG_BOUNDARY riscv_function_arg_boundary
9360+
#undef TARGET_FNTYPE_ABI
9361+
#define TARGET_FNTYPE_ABI riscv_fntype_abi
91629362

91639363
#undef TARGET_SHRINK_WRAP_GET_SEPARATE_COMPONENTS
91649364
#define TARGET_SHRINK_WRAP_GET_SEPARATE_COMPONENTS \

gcc/config/riscv/riscv.h

+25
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
2222
#ifndef GCC_RISCV_H
2323
#define GCC_RISCV_H
2424

25+
#include <stdbool.h>
2526
#include "config/riscv/riscv-opts.h"
2627

2728
/* Target CPU builtins. */
@@ -666,13 +667,18 @@ enum reg_class
666667

667668
#define MAX_ARGS_IN_REGISTERS (riscv_abi == ABI_ILP32E ? 6 : 8)
668669

670+
#define MAX_ARGS_IN_VECTOR_REGISTERS (16)
671+
#define MAX_ARGS_IN_MASK_REGISTERS (1)
672+
669673
/* Symbolic macros for the first/last argument registers. */
670674

671675
#define GP_ARG_FIRST (GP_REG_FIRST + 10)
672676
#define GP_ARG_LAST (GP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1)
673677
#define GP_TEMP_FIRST (GP_REG_FIRST + 5)
674678
#define FP_ARG_FIRST (FP_REG_FIRST + 10)
675679
#define FP_ARG_LAST (FP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1)
680+
#define V_ARG_FIRST (V_REG_FIRST + 8)
681+
#define V_ARG_LAST (V_ARG_FIRST + MAX_ARGS_IN_VECTOR_REGISTERS - 1)
676682

677683
#define CALLEE_SAVED_REG_NUMBER(REGNO) \
678684
((REGNO) >= 8 && (REGNO) <= 9 ? (REGNO) - 8 : \
@@ -696,14 +702,33 @@ enum reg_class
696702
(IN_RANGE ((N), GP_ARG_FIRST, GP_ARG_LAST) \
697703
|| (UNITS_PER_FP_ARG && IN_RANGE ((N), FP_ARG_FIRST, FP_ARG_LAST)))
698704

705+
/* Define the standard RISC-V calling convention and variants. */
706+
707+
enum riscv_cc
708+
{
709+
RISCV_CC_BASE = 0, /* Base standard RISC-V ABI. */
710+
RISCV_CC_V, /* For functions that pass or return values in V registers. */
711+
RISCV_CC_UNKNOWN
712+
};
713+
699714
typedef struct {
715+
/* The calling convention that current function used. */
716+
enum riscv_cc variant_cc;
717+
700718
/* Number of integer registers used so far, up to MAX_ARGS_IN_REGISTERS. */
701719
unsigned int num_gprs;
702720

703721
/* Number of floating-point registers used so far, likewise. */
704722
unsigned int num_fprs;
705723

706724
int rvv_psabi_warning;
725+
726+
/* Number of mask registers used so far, up to MAX_ARGS_IN_MASK_REGISTERS. */
727+
unsigned int num_mrs;
728+
729+
/* The used state of args in vector registers, true for used by prev arg,
730+
initial to false. */
731+
bool used_vrs[MAX_ARGS_IN_VECTOR_REGISTERS];
707732
} CUMULATIVE_ARGS;
708733

709734
/* Initialize a variable CUM of type CUMULATIVE_ARGS

0 commit comments

Comments
 (0)