GEN_INT (offset2))));
}
+/* Return true if VLS mode MODE fits in general purpose registers per the
+ psABI. The psABI allows aggregates up to 2 * XLEN bits to be passed in
+ GPRs. */
+
+static bool
+riscv_vls_mode_fits_in_gprs_p (machine_mode mode)
+{
+ return riscv_vls_mode_p (mode)
+ && known_le (GET_MODE_SIZE (mode), 2 * UNITS_PER_WORD);
+}
+
static rtx
riscv_pass_vls_aggregate_in_gpr (struct riscv_arg_info *info, machine_mode mode,
unsigned gpr_base)
unsigned vls_size = GET_MODE_SIZE (mode).to_constant ();
unsigned gpr_size = GET_MODE_SIZE (Xmode);
- if (IN_RANGE (vls_size, 0, gpr_size * 2))
+ if (riscv_vls_mode_fits_in_gprs_p (mode))
{
count = riscv_v_vls_mode_aggregate_gpr_count (vls_size, gpr_size);
+ unsigned gprs_left = MAX_ARGS_IN_REGISTERS - info->gpr_offset;
- if (count + info->gpr_offset <= MAX_ARGS_IN_REGISTERS)
+ if (count <= gprs_left)
{
+ /* Entire VLS fits in remaining GPRs. */
regnum = gpr_base + info->gpr_offset;
info->num_gprs = count;
gpr_mode = riscv_v_vls_to_gpr_mode (vls_size);
}
+ else if (gprs_left > 0)
+ {
+ /* Per the psABI, split between GPRs and stack:
+ "if only one register is available, the first XLEN bits are
+ passed in a register and the remaining bits are passed on
+ the stack." */
+ regnum = gpr_base + info->gpr_offset;
+ info->num_gprs = gprs_left;
+ info->stack_p = true;
+ gpr_mode = Xmode;
+ }
}
if (!regnum)
/* 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. */
+ pass or return in registers, then the argument may be passed by reference
+ or through the stack. 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,
info->gpr_offset = cum->num_gprs;
info->fpr_offset = cum->num_fprs;
- /* Passed by reference when the scalable vector argument is anonymous. */
- if (riscv_vector_mode_p (mode) && !named)
+ /* Passed by reference when the scalable vector argument is anonymous.
+ VLS modes <= 2*XLEN follow regular aggregate rules per the psABI. */
+ if (riscv_vector_mode_p (mode) && !named
+ && !riscv_vls_mode_fits_in_gprs_p (mode))
return NULL_RTX;
if (named)
if (!named && num_bytes != 0 && alignment > BITS_PER_WORD)
info->gpr_offset += info->gpr_offset & 1;
- /* Partition the argument between registers and stack. */
- info->num_fprs = 0;
- info->num_gprs = MIN (num_words, MAX_ARGS_IN_REGISTERS - info->gpr_offset);
- info->stack_p = (num_words - info->num_gprs) != 0;
+ if (riscv_vls_mode_p (mode))
+ return riscv_pass_vls_aggregate_in_gpr (info, mode, gpr_base);
+ else
+ {
+ /* Partition the argument between registers and stack. */
+ info->num_fprs = 0;
+ info->num_gprs
+ = MIN (num_words, MAX_ARGS_IN_REGISTERS - info->gpr_offset);
+ info->stack_p = (num_words - info->num_gprs) != 0;
- if (info->num_gprs || return_p)
- return gen_rtx_REG (mode, gpr_base + info->gpr_offset);
+ if (info->num_gprs || return_p)
+ return gen_rtx_REG (mode, gpr_base + info->gpr_offset);
+ }
return NULL_RTX;
}
if (info.num_fprs)
return false;
- /* Don't pass by reference if we can use general register(s) for vls. */
- if (info.num_gprs && riscv_vls_mode_p (arg.mode))
- 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;
}
/* Passed by reference when:
- 1. The scalable vector argument is anonymous.
- 2. Args cannot be passed through vector registers. */
- if (riscv_vector_mode_p (arg.mode))
+ (1) The scalable vector argument is anonymous.
+ (2) Args cannot be passed through vector registers.
+ VLS modes <= 2*XLEN follow regular aggregate rules per the psABI. */
+ if (riscv_vector_mode_p (arg.mode)
+ && !riscv_vls_mode_fits_in_gprs_p (arg.mode))
return true;
/* Pass by reference if the data do not fit in two integer registers. */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+/* Test that small VLS types (<= 2*XLEN = 128 bits) are passed in GPRs
+ per the psABI, not by reference. */
+
+typedef int __attribute__((vector_size(8))) v2si;
+
+v2si test_vls_in_gpr (int a0, int a1, v2si a2)
+{
+ return a2;
+}
+
+/* The 8-byte VLS vector should be passed in a2 and returned in a0. */
+/* { dg-final { scan-assembler-times "mv\ta0,a2" 1 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gcv -mabi=ilp32d -O2" } */
+
+/* Test that VLS types > 2*XLEN (> 64 bits for rv32) are passed
+ by reference, not in GPRs. */
+
+typedef int __attribute__((vector_size(16))) v4si;
+
+v4si test_vls_by_reference_rv32 (int a0, v4si a1)
+{
+ return a1;
+}
+
+/* The 16-byte VLS vector should be passed by reference on rv32
+ (since 16 bytes > 2*4 = 8 bytes). */
+/* { dg-final { scan-assembler "vle32.v\tv\[0-9\]+,0\\(a2\\)" } } */
+/* { dg-final { scan-assembler "vse32.v\tv\[0-9\]+,0\\(a0\\)" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+/* Test return value: small VLS types should be returned in GPRs. */
+
+typedef int __attribute__((vector_size(8))) v2si;
+
+v2si make_vls (void)
+{
+ v2si v = {1, 2};
+ return v;
+}
+
+/* The return value should use a0. */
+/* { dg-final { scan-assembler "ld\ta0," } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gcv -mabi=ilp32d -O2" } */
+
+/* Test that 8-byte VLS is split between GPR and stack when only
+ one GPR is available on rv32. Per the psABI: "if only one register
+ is available, the first XLEN bits are passed in a register and the
+ remaining bits are passed on the stack." */
+
+typedef int __attribute__((vector_size(8))) v2si;
+
+v2si test_vls_gpr_stack_split_rv32 (int a0, int a1, int a2, int a3,
+ int a4, int a5, int a6, v2si a7)
+{
+ return a7;
+}
+
+/* a0-a6 use 7 GPRs, leaving only a7. The 8-byte VLS should be
+ split: first 4 bytes in a7, remaining 4 bytes on stack. */
+/* { dg-final { scan-assembler "sw\ta7," } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gcv -mabi=ilp32d -O2" } */
+
+/* Test stack split with two 8-byte VLS arguments on rv32.
+ First VLS splits between a7 and stack, second is fully on stack. */
+
+typedef int __attribute__((vector_size(8))) v2si;
+
+v2si test_vls_gpr_stack_split2_rv32 (int a0, int a1, int a2, int a3,
+ int a4, int a5, int a6, v2si a7,
+ v2si a8)
+{
+ v2si res = a7 + a8;
+ return res;
+}
+
+/* a7 splits (4 bytes in a7, 4 on stack), a8 fully on stack. */
+/* { dg-final { scan-assembler "sw\ta7," } } */
+/* { dg-final { scan-assembler "lw\t\[at\]\[0-9\]+,\[0-9\]+\\(sp\\)" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gcv -mabi=ilp32d -O2" } */
+
+typedef int v2si __attribute__ ((vector_size (8)));
+int test (int accumulator, int dummy, v2si v1, v2si v2, v2si v3, v2si v4)
+{
+ accumulator &= v4[0] & v4[1];
+ return accumulator;
+}
+
+/* v4 should be passed on the stack. */
+/* { dg-final { scan-assembler "vle32.v\tv\[0-9\]+,0\\(sp\\)" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+/* Test that 16-byte VLS types (= 2*XLEN for rv64) are passed in 2 GPRs. */
+
+typedef long __attribute__((vector_size(16))) v2di;
+
+v2di test_vls_two_gprs (int a0, v2di a1)
+{
+ return a1;
+}
+
+/* The 16-byte VLS vector is passed in a1,a2 and returned in a0,a1. */
+/* { dg-final { scan-assembler "sd\ta1," } } */
+/* { dg-final { scan-assembler "sd\ta2," } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+/* Test that small VLS types are passed in GPRs even when filling
+ the argument registers. */
+
+typedef int __attribute__((vector_size(8))) v2si;
+
+v2si test_vls_multiple_gprs (int a0, int a1, int a2, int a3,
+ int a4, int a5, v2si a6)
+{
+ return a6;
+}
+
+/* a0-a5 are used by ints, the 8-byte VLS should use a6. */
+/* { dg-final { scan-assembler-times "mv\ta0,a6" 1 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+/* Test that 16-byte VLS is split between GPR and stack when only
+ one GPR is available. Per the psABI: "if only one register is
+ available, the first XLEN bits are passed in a register and the
+ remaining bits are passed on the stack." */
+
+typedef long __attribute__((vector_size(16))) v2di;
+
+v2di test_vls_gpr_stack_split (int a0, int a1, int a2, int a3,
+ int a4, int a5, int a6, v2di a7)
+{
+ return a7;
+}
+
+/* a0-a6 use 7 GPRs, leaving only a7. The 16-byte VLS should be
+ split: first 8 bytes in a7, remaining 8 bytes on stack. */
+/* { dg-final { scan-assembler "sd\ta7," } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+/* Test that 16-byte VLS is split between GPR and stack when only
+ one GPR is available. Per the psABI: "if only one register is
+ available, the first XLEN bits are passed in a register and the
+ remaining bits are passed on the stack." */
+
+typedef long __attribute__((vector_size(16))) v2di;
+
+v2di test_vls_gpr_stack_split2 (int a0, int a1, int a2, int a3,
+ int a4, int a5, int a6, v2di a7,
+ v2di a8)
+{
+ v2di res = a7 + a8;
+ return res;
+}
+
+/* a0-a6 use 7 GPRs, leaving only a7. The 16-byte VLS should be
+ split: first 8 bytes in a7, remaining 8 bytes on stack. */
+/* { dg-final { scan-assembler "sd\ta7," } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+int test (int accumulator, int dummy, v4si v1, v4si v2, v4si v3, v4si v4)
+{
+ accumulator &= v4[0] & v4[1] & v4[2] & v4[3];
+ return accumulator;
+}
+
+/* v4 should be passed on the stack. */
+/* { dg-final { scan-assembler "vle32.v\tv\[0-9\]+,0\\(sp\\)" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+/* Test that 4-byte VLS types (< XLEN) are passed in a single GPR. */
+
+typedef short __attribute__((vector_size(4))) v2hi;
+
+v2hi test_vls_small (int a0, v2hi a1)
+{
+ return a1;
+}
+
+/* The 4-byte VLS vector should be passed in a1. */
+/* { dg-final { scan-assembler-times "mv\ta0,a1" 1 } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gcv -mabi=ilp32d -O2" } */
+
+/* Test that 8-byte VLS types (= 2*XLEN for rv32) are passed in 2 GPRs. */
+
+typedef int __attribute__((vector_size(8))) v2si;
+
+v2si test_vls_two_gprs_rv32 (int a0, v2si a1)
+{
+ return a1;
+}
+
+/* The 8-byte VLS vector is passed in a1,a2 and returned in a0,a1 on rv32. */
+/* { dg-final { scan-assembler "mv\ta0,a1" } } */
+/* { dg-final { scan-assembler "mv\ta1,a2" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */
+
+/* Test that VLS types > 2*XLEN (> 128 bits for rv64) are passed
+ by reference, not in GPRs. */
+
+typedef long __attribute__((vector_size(32))) v4di;
+
+v4di test_vls_by_reference (int a0, v4di a1)
+{
+ return a1;
+}
+
+/* The 32-byte VLS vector should be passed by reference.
+ Return value pointer in a0, argument pointer in a2 (a1 holds a0). */
+/* { dg-final { scan-assembler "vle64.v\tv\[0-9\]+,0\\(a2\\)" } } */
+/* { dg-final { scan-assembler "vse64.v\tv\[0-9\]+,0\\(a0\\)" } } */