* [PATCH 1/2] RISC-V Vector Extension Support
@ 2025-11-07 16:55 Kirill Radkin
2025-11-07 16:55 ` [PATCH 2/2] RISC-V Vector Extension Support Testing Kirill Radkin
0 siblings, 1 reply; 2+ messages in thread
From: Kirill Radkin @ 2025-11-07 16:55 UTC (permalink / raw)
To: gdb-patches; +Cc: Kirill Radkin
This patch adds support for the RISC-V Vector Extension (RVV) and the RISC-V
Vector ABI to GDB, for both gdb and gdbserver (so, it's available for cross and
native debugging). It is now possible to inspect and modify vector registers
directly. Furthermore, GDB supports evaluating variables with RISC-V vector
types (such as vint32m4_t) and calling functions that take vector arguments
using the print and call GDB commands. Patch also includes tests targeting RVV
support, tested on targets with different vlen (74, 128, 1024). Implementation
is tested on system QEMU (Linux) and on OpenOCD + spike configuration.
---
gdb/arch/riscv.c | 9 +-
gdb/arch/riscv.h | 18 +-
gdb/features/riscv/rvv.c | 113 ++++++
gdb/gdbtypes.c | 41 ++
gdb/nat/riscv-linux-ptrace.h | 31 ++
gdb/nat/riscv-linux-tdesc.c | 33 ++
gdb/riscv-linux-nat.c | 164 ++++++++
gdb/riscv-regs.h | 79 ++++
gdb/riscv-tdep.c | 758 ++++++++++++++++++++++++++++++-----
gdb/riscv-tdep.h | 63 +--
gdbserver/linux-riscv-low.cc | 140 +++++--
gdbsupport/common-utils.h | 2 +
include/elf/common.h | 1 +
13 files changed, 1255 insertions(+), 197 deletions(-)
create mode 100644 gdb/features/riscv/rvv.c
create mode 100644 gdb/nat/riscv-linux-ptrace.h
create mode 100644 gdb/riscv-regs.h
diff --git a/gdb/arch/riscv.c b/gdb/arch/riscv.c
index c698fa2b62e..d5d41fe6382 100644
--- a/gdb/arch/riscv.c
+++ b/gdb/arch/riscv.c
@@ -22,6 +22,7 @@
#include "../features/riscv/64bit-cpu.c"
#include "../features/riscv/32bit-fpu.c"
#include "../features/riscv/64bit-fpu.c"
+#include "../features/riscv/rvv.c"
#include "../features/riscv/rv32e-xregs.c"
#ifndef GDBSERVER
@@ -82,11 +83,9 @@ riscv_create_target_description (const struct riscv_gdbarch_features features)
else if (features.flen == 8)
regnum = create_feature_riscv_64bit_fpu (tdesc.get (), regnum);
- /* Currently GDB only supports vector features coming from remote
- targets. We don't support creating vector features on native targets
- (yet). */
- if (features.vlen != 0)
- error (_("unable to create vector feature"));
+ if (features.vlenb != 0)
+ regnum = create_feature_riscv_rvv (tdesc.get (), features.vlenb,
+ features.xlen);
return tdesc;
}
diff --git a/gdb/arch/riscv.h b/gdb/arch/riscv.h
index bc95e72bb35..8b4e4b3c8cb 100644
--- a/gdb/arch/riscv.h
+++ b/gdb/arch/riscv.h
@@ -51,7 +51,7 @@ struct riscv_gdbarch_features
target should be 16 and 4 for an embedded subset compliant target (with
'Zve32*' extension), but GDB doesn't currently mind, and will accept any
vector size. */
- int vlen = 0;
+ int vlenb = 0;
/* When true this target is RV32E. */
bool embedded = false;
@@ -68,9 +68,8 @@ struct riscv_gdbarch_features
/* Equality operator. */
bool operator== (const struct riscv_gdbarch_features &rhs) const
{
- return (xlen == rhs.xlen && flen == rhs.flen
- && embedded == rhs.embedded && vlen == rhs.vlen
- && has_fflags_reg == rhs.has_fflags_reg
+ return (xlen == rhs.xlen && flen == rhs.flen && embedded == rhs.embedded
+ && vlenb == rhs.vlenb && has_fflags_reg == rhs.has_fflags_reg
&& has_frm_reg == rhs.has_frm_reg
&& has_fcsr_reg == rhs.has_fcsr_reg);
}
@@ -84,13 +83,10 @@ struct riscv_gdbarch_features
/* Used by std::unordered_map to hash feature sets. */
std::size_t hash () const noexcept
{
- std::size_t val = ((embedded ? 1 : 0) << 10
- | (has_fflags_reg ? 1 : 0) << 11
- | (has_frm_reg ? 1 : 0) << 12
- | (has_fcsr_reg ? 1 : 0) << 13
- | (xlen & 0x1f) << 5
- | (flen & 0x1f) << 0
- | (vlen & 0x3fff) << 14);
+ std::size_t val
+ = ((embedded ? 1 : 0) << 10 | (has_fflags_reg ? 1 : 0) << 11
+ | (has_frm_reg ? 1 : 0) << 12 | (has_fcsr_reg ? 1 : 0) << 13
+ | (xlen & 0x1f) << 5 | (flen & 0x1f) << 0 | (vlenb & 0x3fff) << 14);
return val;
}
};
diff --git a/gdb/features/riscv/rvv.c b/gdb/features/riscv/rvv.c
new file mode 100644
index 00000000000..a7a3adb5e65
--- /dev/null
+++ b/gdb/features/riscv/rvv.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbsupport/gdb_assert.h"
+#include "gdbsupport/tdesc.h"
+#include "gdb/riscv-regs.h"
+#include <vector>
+
+/* This file is NOT auto generated from xml.
+ 'create_feature_riscv_rvv' creates a RISCV Vector Extension feature.
+ 'vlenb' is a vector register size in bytes */
+
+struct vector_field_type_info
+{
+ const char *element_type_name;
+ const char *vector_type_name;
+ int element_count;
+};
+
+static void
+create_vector_register_type (tdesc_feature *feature,
+ std::vector<vector_field_type_info> &types_info,
+ const char *type_name)
+{
+ tdesc_type *element_type;
+
+ for (auto &&type_info : types_info)
+ {
+ if (type_info.element_count == -1)
+ continue;
+ element_type = tdesc_named_type (feature, type_info.element_type_name);
+ tdesc_create_vector (feature, type_info.vector_type_name, element_type,
+ type_info.element_count);
+ }
+
+ tdesc_type_with_fields *type_with_fields
+ = tdesc_create_union (feature, type_name);
+
+ for (auto &&type_info : types_info)
+ {
+ if (type_info.element_count == -1)
+ continue;
+ element_type = tdesc_named_type (feature, type_info.vector_type_name);
+ tdesc_add_field (type_with_fields, type_info.vector_type_name,
+ element_type);
+ }
+}
+
+static int
+create_feature_riscv_rvv (target_desc *result, int vlenb, int xlen)
+{
+ gdb_assert (result);
+ gdb_assert (xlen == 4 || xlen == 8);
+ gdb_assert (vlenb >= 4 && ((vlenb & (vlenb - 1)) == 0));
+
+ int v_bitsize = 8 * vlenb;
+ int x_bitsize = 8 * xlen;
+
+ tdesc_feature *csr_feature
+ = tdesc_create_feature (result, "org.gnu.gdb.riscv.csr");
+ tdesc_create_reg (csr_feature, "vstart", RISCV_CSR_VSTART_REGNUM, 1, NULL,
+ x_bitsize, "int");
+ tdesc_create_reg (csr_feature, "vcsr", RISCV_CSR_VCSR_REGNUM, 1, NULL,
+ x_bitsize, "int");
+ tdesc_create_reg (csr_feature, "vl", RISCV_CSR_VL_REGNUM, 1, NULL, x_bitsize,
+ "int");
+ tdesc_create_reg (csr_feature, "vtype", RISCV_CSR_VTYPE_REGNUM, 1, NULL,
+ x_bitsize, "int");
+ tdesc_create_reg (csr_feature, "vlenb", RISCV_CSR_VLENB_REGNUM, 1, NULL,
+ x_bitsize, "int");
+
+ tdesc_feature *vector_feature
+ = tdesc_create_feature (result, "org.gnu.gdb.riscv.vector");
+
+ std::vector<vector_field_type_info> elements_types_info
+ = { { "int8", "i8", vlenb },
+ { "int16", "i16", vlenb / 2 },
+ { "int32", "i32", vlenb / 4 },
+ { "int64", "i64", (vlenb >= 8) ? vlenb / 8 : -1 },
+ { "ieee_half", "half", vlenb / 2 },
+ { "ieee_single", "f32", vlenb / 4 },
+ { "ieee_double", "f64", (vlenb >= 8) ? vlenb / 8 : -1 } };
+
+ create_vector_register_type (vector_feature, elements_types_info, "rvv");
+
+ int regnum = RISCV_V0_REGNUM;
+
+ constexpr const char *vec_reg_names[]
+ = { "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
+ "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
+ "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
+ "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" };
+
+ for (int i = 0; i < 32; ++i)
+ tdesc_create_reg (vector_feature, vec_reg_names[i], regnum++, 1, NULL,
+ v_bitsize, "rvv");
+
+ return regnum;
+}
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 63d6f4c0ceb..ba356fefdea 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -2151,6 +2151,21 @@ is_dynamic_type_internal (struct type *type, bool top_level)
}
}
break;
+
+ case TYPE_CODE_FUNC:
+ {
+ /* If the type of value returned by function is dynamic, we should mark
+ func as dynamic to later resolve this dynamic type */
+ if (type->target_type ()
+ && is_dynamic_type_internal (type->target_type (), false))
+ return true;
+
+ /* Same for function arguments */
+ for (int i = 0; i < type->num_fields (); ++i)
+ if (is_dynamic_type_internal (type->field (i).type (), false))
+ return true;
+ }
+ break;
}
return false;
@@ -2873,6 +2888,25 @@ resolve_dynamic_struct (struct type *type,
return resolved_type;
}
+/* Resolve dynamic function's arguments/returned value types */
+static struct type *
+resolve_dynamic_func (struct type *type, const property_addr_info *addr_stack,
+ const frame_info_ptr &frame, bool top_level)
+{
+ gdb_assert (type->code () == TYPE_CODE_FUNC);
+
+ struct type *resolved_type = copy_type (type);
+
+ resolved_type->set_target_type (resolve_dynamic_type_internal (
+ type->target_type (), addr_stack, frame, top_level));
+
+ for (int i = 0; i < resolved_type->num_fields (); i++)
+ resolved_type->field (i).set_type (resolve_dynamic_type_internal (
+ resolved_type->field (i).type (), addr_stack, frame, top_level));
+
+ return resolved_type;
+}
+
/* Worker for resolved_dynamic_type. */
static struct type *
@@ -2971,6 +3005,13 @@ resolve_dynamic_type_internal (struct type *type,
case TYPE_CODE_STRUCT:
resolved_type = resolve_dynamic_struct (type, addr_stack, frame);
break;
+
+ case TYPE_CODE_FUNC:
+ /* If func was marked as dynamic, that means that it have dynamic
+ arguments or returned value*/
+ resolved_type
+ = resolve_dynamic_func (type, addr_stack, frame, top_level);
+ break;
}
}
diff --git a/gdb/nat/riscv-linux-ptrace.h b/gdb/nat/riscv-linux-ptrace.h
new file mode 100644
index 00000000000..4ec67093936
--- /dev/null
+++ b/gdb/nat/riscv-linux-ptrace.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef NAT_RISCV_LINUX_HW_PTRACE_H
+#define NAT_RISCV_LINUX_HW_PTRACE_H
+
+struct __riscv_v_regset_state
+{
+ unsigned long vstart;
+ unsigned long vl;
+ unsigned long vtype;
+ unsigned long vcsr;
+ unsigned long vlenb;
+ char vreg[];
+};
+
+#endif // NAT_RISCV_LINUX_HW_PTRACE_H
diff --git a/gdb/nat/riscv-linux-tdesc.c b/gdb/nat/riscv-linux-tdesc.c
index 254a25ccefe..e7d6d29eea1 100644
--- a/gdb/nat/riscv-linux-tdesc.c
+++ b/gdb/nat/riscv-linux-tdesc.c
@@ -30,6 +30,27 @@
# define NFPREG 33
#endif
+// RISC-V Hardware Probing Syscall Number
+#ifndef NR_riscv_hwprobe
+#ifndef NR_arch_specific_syscall
+#define NR_arch_specific_syscall 244
+#endif
+#define NR_riscv_hwprobe (NR_arch_specific_syscall + 14)
+#endif
+
+#ifndef RISCV_HWPROBE_KEY_IMA_EXT_0
+// A bitmask containing the supported extensions
+#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
+// Bit that indicate RISC-V Vector extension support
+#define RISCV_HWPROBE_IMA_V (1 << 2)
+#endif
+
+struct riscv_hwprobe
+{
+ int64_t key;
+ uint64_t value;
+};
+
/* See nat/riscv-linux-tdesc.h. */
struct riscv_gdbarch_features
@@ -78,5 +99,17 @@ riscv_linux_read_features (int tid)
break;
}
+ features.vlenb = 0;
+
+ static struct riscv_hwprobe query[] = { { RISCV_HWPROBE_KEY_IMA_EXT_0, 0 } };
+
+ if ((syscall (NR_riscv_hwprobe, query, 1, 0, NULL, 0) == 0)
+ && (query[0].value & RISCV_HWPROBE_IMA_V))
+ {
+ int reg = 0;
+ asm volatile ("csrr %[vlenb], vlenb" : [vlenb] "=r"(reg));
+ features.vlenb = reg;
+ }
+
return features;
}
diff --git a/gdb/riscv-linux-nat.c b/gdb/riscv-linux-nat.c
index 89f1ddc1b17..459654565a3 100644
--- a/gdb/riscv-linux-nat.c
+++ b/gdb/riscv-linux-nat.c
@@ -24,10 +24,13 @@
#include "elf/common.h"
+#include "nat/riscv-linux-ptrace.h"
#include "nat/riscv-linux-tdesc.h"
#include <sys/ptrace.h>
+#include <memory>
+
/* Work around glibc header breakage causing ELF_NFPREG not to be usable. */
#ifndef NFPREG
# define NFPREG 33
@@ -132,6 +135,63 @@ supply_fpregset (struct regcache *regcache, const prfpregset_t *fpregs)
supply_fpregset_regnum (regcache, fpregs, -1);
}
+static void
+supply_vecregset_regnum (regcache *regcache,
+ const __riscv_v_regset_state *vecregs, int regnum)
+{
+ gdb_assert (vecregs->vlenb > 0);
+ if ((regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM))
+ {
+ regcache->raw_supply (
+ regnum, vecregs->vreg + vecregs->vlenb * (regnum - RISCV_V0_REGNUM));
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VSTART_REGNUM)
+ {
+ regcache->raw_supply (regnum, &vecregs->vstart);
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VCSR_REGNUM)
+ {
+ regcache->raw_supply (regnum, &vecregs->vcsr);
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VL_REGNUM)
+ {
+ regcache->raw_supply (regnum, &vecregs->vl);
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VTYPE_REGNUM)
+ {
+ regcache->raw_supply (regnum, &vecregs->vtype);
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VLENB_REGNUM)
+ {
+ regcache->raw_supply (regnum, &vecregs->vlenb);
+ return;
+ }
+
+ if (regnum == -1)
+ {
+ regcache->raw_supply (RISCV_CSR_VSTART_REGNUM, &vecregs->vstart);
+ regcache->raw_supply (RISCV_CSR_VCSR_REGNUM, &vecregs->vcsr);
+ regcache->raw_supply (RISCV_CSR_VL_REGNUM, &vecregs->vl);
+ regcache->raw_supply (RISCV_CSR_VTYPE_REGNUM, &vecregs->vtype);
+ regcache->raw_supply (RISCV_CSR_VLENB_REGNUM, &vecregs->vlenb);
+
+ for (int i = RISCV_V0_REGNUM; i <= RISCV_V31_REGNUM; i++)
+ regcache->raw_supply (i, vecregs->vreg
+ + vecregs->vlenb * (i - RISCV_V0_REGNUM));
+ return;
+ }
+}
+
/* Copy general purpose register REGNUM (or all gp regs if REGNUM == -1)
from REGCACHE into regset GREGS. */
@@ -195,6 +255,63 @@ fill_fpregset (const struct regcache *regcache, prfpregset_t *fpregs,
}
}
+static void
+fill_vecregset_regnum (regcache *regcache, __riscv_v_regset_state *vecregs,
+ int regnum)
+{
+ if ((regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM))
+ {
+ regcache->raw_collect (
+ regnum, vecregs->vreg + vecregs->vlenb * (regnum - RISCV_V0_REGNUM));
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VSTART_REGNUM)
+ {
+ regcache->raw_collect (regnum, &vecregs->vstart);
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VCSR_REGNUM)
+ {
+ regcache->raw_collect (regnum, &vecregs->vcsr);
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VL_REGNUM)
+ {
+ regcache->raw_collect (regnum, &vecregs->vl);
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VTYPE_REGNUM)
+ {
+ regcache->raw_collect (regnum, &vecregs->vtype);
+ return;
+ }
+
+ if (regnum == RISCV_CSR_VLENB_REGNUM)
+ {
+ regcache->raw_collect (regnum, &vecregs->vlenb);
+ return;
+ }
+
+ if (regnum == -1)
+ {
+ regcache->raw_collect (RISCV_CSR_VSTART_REGNUM, &vecregs->vstart);
+ regcache->raw_collect (RISCV_CSR_VCSR_REGNUM, &vecregs->vcsr);
+ regcache->raw_collect (RISCV_CSR_VL_REGNUM, &vecregs->vl);
+ regcache->raw_collect (RISCV_CSR_VTYPE_REGNUM, &vecregs->vtype);
+ regcache->raw_collect (RISCV_CSR_VLENB_REGNUM, &vecregs->vlenb);
+
+ for (int i = RISCV_V0_REGNUM; i <= RISCV_V31_REGNUM; i++)
+ regcache->raw_collect (
+ i, vecregs->vreg + vecregs->vlenb * (i - RISCV_V0_REGNUM));
+
+ return;
+ }
+}
+
/* Return a target description for the current target. */
const struct target_desc *
@@ -261,6 +378,29 @@ riscv_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
regcache->raw_supply_zeroed (RISCV_CSR_MISA_REGNUM);
}
+ if (riscv_is_vpr_or_vcsr (regnum) || (regnum == -1))
+ {
+ int vecreg_size = register_size (regcache->arch (), RISCV_V0_REGNUM);
+ std::vector<char> vregs_buff (sizeof (__riscv_v_regset_state)
+ + vecreg_size * 32);
+
+ __riscv_v_regset_state *vregs_state
+ = (__riscv_v_regset_state *)vregs_buff.data ();
+
+ iovec iov;
+ iov.iov_base = vregs_state;
+ iov.iov_len = sizeof (struct __riscv_v_regset_state) + 32 * vecreg_size;
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_RISCV_VECTOR,
+ (PTRACE_TYPE_ARG3)&iov)
+ == 0
+ && vregs_state->vlenb > 0)
+ {
+ gdb_assert (vregs_state->vlenb == vecreg_size);
+ supply_vecregset_regnum (regcache, vregs_state, regnum);
+ }
+ }
+
/* Access to other CSRs has potential security issues, don't support them for
now. */
}
@@ -323,6 +463,30 @@ riscv_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
}
}
+ if (riscv_is_vpr_or_vcsr (regnum) || (regnum == -1))
+ {
+ int vecreg_size = register_size (regcache->arch (), RISCV_V0_REGNUM);
+ std::vector<char> vregs_buff (sizeof (__riscv_v_regset_state)
+ + vecreg_size * 32);
+
+ __riscv_v_regset_state *vregs_state
+ = (__riscv_v_regset_state *)vregs_buff.data ();
+
+ iovec iov;
+ iov.iov_base = vregs_state;
+ iov.iov_len = sizeof (struct __riscv_v_regset_state) + 32 * vecreg_size;
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_RISCV_VECTOR,
+ (PTRACE_TYPE_ARG3)&iov)
+ == 0)
+ {
+ fill_vecregset_regnum (regcache, vregs_state, regnum);
+
+ ptrace (PTRACE_SETREGSET, tid, NT_RISCV_VECTOR,
+ (PTRACE_TYPE_ARG3)&iov);
+ }
+ }
+
/* Access to CSRs has potential security issues, don't support them for
now. */
}
diff --git a/gdb/riscv-regs.h b/gdb/riscv-regs.h
new file mode 100644
index 00000000000..e6b06445e6e
--- /dev/null
+++ b/gdb/riscv-regs.h
@@ -0,0 +1,79 @@
+/* Target-dependent header for the RISC-V architecture, for GDB, the
+ GNU Debugger.
+
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_RISCV_REGS_H
+#define GDB_RISCV_REGS_H
+
+/* RiscV register numbers. */
+enum
+{
+ RISCV_ZERO_REGNUM = 0, /* Read-only register, always 0. */
+ RISCV_RA_REGNUM = 1, /* Return Address. */
+ RISCV_SP_REGNUM = 2, /* Stack Pointer. */
+ RISCV_GP_REGNUM = 3, /* Global Pointer. */
+ RISCV_TP_REGNUM = 4, /* Thread Pointer. */
+ RISCV_FP_REGNUM = 8, /* Frame Pointer. */
+ RISCV_A0_REGNUM = 10, /* First argument. */
+ RISCV_A1_REGNUM = 11, /* Second argument. */
+ RISCV_A2_REGNUM = 12, /* Third argument. */
+ RISCV_A3_REGNUM = 13, /* Forth argument. */
+ RISCV_A4_REGNUM = 14, /* Fifth argument. */
+ RISCV_A5_REGNUM = 15, /* Sixth argument. */
+ RISCV_A7_REGNUM = 17, /* Register to pass syscall number. */
+ RISCV_PC_REGNUM = 32, /* Program Counter. */
+
+ RISCV_NUM_INTEGER_REGS = 32,
+
+ RISCV_FIRST_FP_REGNUM = 33, /* First Floating Point Register */
+ RISCV_FA0_REGNUM = 43,
+ RISCV_FA1_REGNUM = RISCV_FA0_REGNUM + 1,
+ RISCV_LAST_FP_REGNUM = 64, /* Last Floating Point Register */
+
+ RISCV_FIRST_CSR_REGNUM = 65, /* First CSR */
+#define DECLARE_CSR(name, num, class, define_version, abort_version) \
+ RISCV_ ## num ## _REGNUM = RISCV_FIRST_CSR_REGNUM + num,
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+ RISCV_LAST_CSR_REGNUM = 4160,
+ RISCV_CSR_LEGACY_MISA_REGNUM = 0xf10 + RISCV_FIRST_CSR_REGNUM,
+
+ RISCV_PRIV_REGNUM = 4161,
+
+ RISCV_V0_REGNUM,
+
+ RISCV_V31_REGNUM = RISCV_V0_REGNUM + 31,
+
+ RISCV_LAST_REGNUM = RISCV_V31_REGNUM
+};
+
+/* RiscV DWARF register numbers. */
+enum
+{
+ RISCV_DWARF_REGNUM_X0 = 0,
+ RISCV_DWARF_REGNUM_X31 = 31,
+ RISCV_DWARF_REGNUM_F0 = 32,
+ RISCV_DWARF_REGNUM_F31 = 63,
+ RISCV_DWARF_REGNUM_V0 = 96,
+ RISCV_DWARF_REGNUM_V31 = 127,
+ RISCV_DWARF_FIRST_CSR = 4096,
+ RISCV_DWARF_LAST_CSR = 8191,
+};
+
+#endif /* GDB_RISCV_REGS_H */
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 76d10a3b298..03587e14418 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -17,6 +17,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#include <regex>
+
#include "extract-store-integer.h"
#include "frame.h"
#include "inferior.h"
@@ -57,6 +59,13 @@
#include "record-full.h"
#include "riscv-ravenscar-thread.h"
+#include <algorithm>
+#include <array>
+#include <charconv>
+#include <list>
+#include <optional>
+#include <unordered_map>
+#include <unordered_set>
#include <vector>
/* The stack must be 16-byte aligned. */
@@ -619,39 +628,23 @@ struct riscv_vector_feature : public riscv_register_feature
riscv_vector_feature ()
: riscv_register_feature (riscv_feature_name_vector)
{
- m_registers = {
- { RISCV_V0_REGNUM + 0, { "v0" } },
- { RISCV_V0_REGNUM + 1, { "v1" } },
- { RISCV_V0_REGNUM + 2, { "v2" } },
- { RISCV_V0_REGNUM + 3, { "v3" } },
- { RISCV_V0_REGNUM + 4, { "v4" } },
- { RISCV_V0_REGNUM + 5, { "v5" } },
- { RISCV_V0_REGNUM + 6, { "v6" } },
- { RISCV_V0_REGNUM + 7, { "v7" } },
- { RISCV_V0_REGNUM + 8, { "v8" } },
- { RISCV_V0_REGNUM + 9, { "v9" } },
- { RISCV_V0_REGNUM + 10, { "v10" } },
- { RISCV_V0_REGNUM + 11, { "v11" } },
- { RISCV_V0_REGNUM + 12, { "v12" } },
- { RISCV_V0_REGNUM + 13, { "v13" } },
- { RISCV_V0_REGNUM + 14, { "v14" } },
- { RISCV_V0_REGNUM + 15, { "v15" } },
- { RISCV_V0_REGNUM + 16, { "v16" } },
- { RISCV_V0_REGNUM + 17, { "v17" } },
- { RISCV_V0_REGNUM + 18, { "v18" } },
- { RISCV_V0_REGNUM + 19, { "v19" } },
- { RISCV_V0_REGNUM + 20, { "v20" } },
- { RISCV_V0_REGNUM + 21, { "v21" } },
- { RISCV_V0_REGNUM + 22, { "v22" } },
- { RISCV_V0_REGNUM + 23, { "v23" } },
- { RISCV_V0_REGNUM + 24, { "v24" } },
- { RISCV_V0_REGNUM + 25, { "v25" } },
- { RISCV_V0_REGNUM + 26, { "v26" } },
- { RISCV_V0_REGNUM + 27, { "v27" } },
- { RISCV_V0_REGNUM + 28, { "v28" } },
- { RISCV_V0_REGNUM + 29, { "v29" } },
- { RISCV_V0_REGNUM + 30, { "v30" } },
- { RISCV_V0_REGNUM + 31, { "v31" } },
+ m_registers = {
+ { RISCV_V0_REGNUM + 0, { "v0" } }, { RISCV_V0_REGNUM + 1, { "v1" } },
+ { RISCV_V0_REGNUM + 2, { "v2" } }, { RISCV_V0_REGNUM + 3, { "v3" } },
+ { RISCV_V0_REGNUM + 4, { "v4" } }, { RISCV_V0_REGNUM + 5, { "v5" } },
+ { RISCV_V0_REGNUM + 6, { "v6" } }, { RISCV_V0_REGNUM + 7, { "v7" } },
+ { RISCV_V0_REGNUM + 8, { "v8" } }, { RISCV_V0_REGNUM + 9, { "v9" } },
+ { RISCV_V0_REGNUM + 10, { "v10" } }, { RISCV_V0_REGNUM + 11, { "v11" } },
+ { RISCV_V0_REGNUM + 12, { "v12" } }, { RISCV_V0_REGNUM + 13, { "v13" } },
+ { RISCV_V0_REGNUM + 14, { "v14" } }, { RISCV_V0_REGNUM + 15, { "v15" } },
+ { RISCV_V0_REGNUM + 16, { "v16" } }, { RISCV_V0_REGNUM + 17, { "v17" } },
+ { RISCV_V0_REGNUM + 18, { "v18" } }, { RISCV_V0_REGNUM + 19, { "v19" } },
+ { RISCV_V0_REGNUM + 20, { "v20" } }, { RISCV_V0_REGNUM + 21, { "v21" } },
+ { RISCV_V0_REGNUM + 22, { "v22" } }, { RISCV_V0_REGNUM + 23, { "v23" } },
+ { RISCV_V0_REGNUM + 24, { "v24" } }, { RISCV_V0_REGNUM + 25, { "v25" } },
+ { RISCV_V0_REGNUM + 26, { "v26" } }, { RISCV_V0_REGNUM + 27, { "v27" } },
+ { RISCV_V0_REGNUM + 28, { "v28" } }, { RISCV_V0_REGNUM + 29, { "v29" } },
+ { RISCV_V0_REGNUM + 30, { "v30" } }, { RISCV_V0_REGNUM + 31, { "v31" } },
};
}
@@ -660,10 +653,14 @@ struct riscv_vector_feature : public riscv_register_feature
RISCV_V0_REGNUM + 31. */
const char *register_name (int regnum) const
{
- gdb_assert (regnum >= RISCV_V0_REGNUM
- && regnum <= RISCV_V0_REGNUM + 31);
- regnum -= RISCV_V0_REGNUM;
- return m_registers[regnum].names[0];
+ gdb_assert (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V0_REGNUM + 31);
+
+ const auto reg_info_it = std::find_if (
+ m_registers.begin (), m_registers.end (),
+ [regnum] (const auto ®_info) { return reg_info.regnum == regnum; });
+ if (reg_info_it == m_registers.end ())
+ gdb_assert_not_reached ("Incorrect vector register number %d", regnum);
+ return reg_info_it->names.front ();
}
/* Check this feature within TDESC, record the registers from this
@@ -679,7 +676,7 @@ struct riscv_vector_feature : public riscv_register_feature
feature set and return. */
if (feature_vector == nullptr)
{
- features->vlen = 0;
+ features->vlenb = 0;
return true;
}
@@ -696,6 +693,9 @@ struct riscv_vector_feature : public riscv_register_feature
int vector_bitsize = -1;
for (const auto ® : m_registers)
{
+ if (reg.regnum < RISCV_V0_REGNUM || reg.regnum > RISCV_V31_REGNUM)
+ continue;
+
int reg_bitsize = -1;
for (const char *name : reg.names)
{
@@ -712,7 +712,7 @@ struct riscv_vector_feature : public riscv_register_feature
return false;
}
- features->vlen = (vector_bitsize / 8);
+ features->vlenb = (vector_bitsize / 8);
return true;
}
};
@@ -793,6 +793,25 @@ riscv_abi_xlen (struct gdbarch *gdbarch)
/* See riscv-tdep.h. */
+int
+riscv_isa_vlenb (struct gdbarch *gdbarch)
+{
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ return tdep->isa_features.vlenb;
+}
+
+/* Return true if GDBARCH is using vector hardware ABI. */
+
+static bool
+riscv_has_vector_abi (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch);
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ return tdep->abi_features.vlenb > 0;
+}
+
+/* See riscv-tdep.h. */
+
int
riscv_isa_flen (struct gdbarch *gdbarch)
{
@@ -1211,7 +1230,7 @@ riscv_print_one_register_info (struct gdbarch *gdbarch,
struct value_print_options opts;
riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
- /* Print the register in hex. */
+ /* Print the register in hex, exclude vector registers. */
get_formatted_print_options (&opts, 'x');
opts.deref_ref = true;
common_val_print (val, file, 0, &opts, current_language);
@@ -1339,17 +1358,45 @@ riscv_print_one_register_info (struct gdbarch *gdbarch,
else
gdb_printf (file, "\tprv:%d [INVALID]", priv);
}
- else
+ else if (regnum == RISCV_CSR_VTYPE_REGNUM)
{
- /* If not a vector register, print it also according to its
- natural format. */
- if (regtype->is_vector () == 0)
- {
- get_user_print_options (&opts);
- opts.deref_ref = true;
- gdb_printf (file, "\t");
- common_val_print (val, file, 0, &opts, current_language);
- }
+ LONGEST d = value_as_long (val);
+ // Values are decoded according to RISC-V Vector Specification
+ unsigned lmul_val = d & 0x7;
+ const char *decoded_lmul_variants[]
+ = { "1", "2", "4", "8", "Reserved", "1/8", "1/4", "1/2" };
+ unsigned sew_val = (d >> 3) & 0x7;
+ const char *decoded_sew_variants[]
+ = { "e8", "e16", "e32", "e64",
+ "Reserved", "Reserved", "Reserved", "Reserved" };
+ unsigned vta = (d >> 6) & 0x1;
+ const char *decoded_vta_variants[] = { "tu", "ta" };
+ unsigned vma = (d >> 7) & 0x1;
+ const char *decoded_vma_variants[] = { "mu", "ma" };
+ int size = register_size (gdbarch, regnum);
+ unsigned xlen = size * 8;
+ unsigned vill = (d >> (xlen - 1)) & 0x1;
+ gdb_printf (file,
+ "\tLMUL:%u (%s) SEW:%u (%s) vta:%u (%s) vma:%u "
+ "(%s) vill:%u",
+ lmul_val, decoded_lmul_variants[lmul_val], sew_val,
+ decoded_sew_variants[sew_val], vta,
+ decoded_vta_variants[vta], vma,
+ decoded_vma_variants[vma], vill);
+ }
+ else if (regnum == RISCV_CSR_VCSR_REGNUM)
+ {
+ LONGEST d = value_as_long (val);
+ unsigned vxsat = d & 1;
+ unsigned vxrm = (d >> 1) & 0b11;
+ gdb_printf (file, "\tVXSAT:%u VXRM:%u", vxsat, vxrm);
+ }
+ else if (regtype->is_vector () == 0)
+ {
+ get_user_print_options (&opts);
+ opts.deref_ref = true;
+ gdb_printf (file, "\t");
+ common_val_print (val, file, 0, &opts, current_language);
}
}
}
@@ -1469,7 +1516,7 @@ riscv_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
return 0;
}
else if (reggroup == vector_reggroup)
- return (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM);
+ return riscv_is_vpr_or_vcsr (regnum);
else
return 0;
}
@@ -2654,18 +2701,25 @@ struct riscv_arg_info
{
/* What type of location this is. */
enum location_type
- {
- /* Argument passed in a register. */
- in_reg,
+ {
+ /* Argument passed in a register. */
+ in_reg,
- /* Argument passed as an on stack argument. */
- on_stack,
+ /* Argument passed in several regs. (For now, used for Vector registers.)
+ The first register in the sequence of registers (used for passing
+ arguments) is passed through the first location (see the comment above
+ struct location), and the last one is passed through the second
+ location. */
+ in_several_regs,
- /* Argument passed by reference. The second location is always
- valid for a BY_REF argument, and describes where the address
- of the BY_REF argument should be placed. */
- by_ref
- } loc_type;
+ /* Argument passed as an on stack argument. */
+ on_stack,
+
+ /* Argument passed by reference. The second location is always
+ valid for a BY_REF argument, and describes where the address
+ of the BY_REF argument should be placed. */
+ by_ref
+ } loc_type;
/* Information that depends on the location type. */
union
@@ -2714,13 +2768,98 @@ struct riscv_arg_reg
/* Nothing. */
}
- /* The GDB register number to use in this set. */
+ /* The GDB register number to use in this set. */
int next_regnum;
- /* The last GDB register number to use in this set. */
+ /* The last GDB register number to use in this set. */
int last_regnum;
};
+struct riscv_vector_arg_reg_interval
+{
+ int first;
+ int last;
+};
+
+struct riscv_vector_arg_reg
+{
+ riscv_vector_arg_reg (int first, int last)
+ : available_intervals{ { first, last } }
+ {
+ /* Nothing. */
+ }
+
+ int
+ get_interval_start (int count, int NFIELDS = 1)
+ {
+ gdb_assert (count % NFIELDS == 0);
+
+ int LMUL = count / NFIELDS;
+ for (auto cur_interval = available_intervals.begin ();
+ cur_interval != available_intervals.end (); ++cur_interval)
+ {
+ int cur_first = cur_interval->first;
+ int cur_last = cur_interval->last;
+ if ((cur_first - RISCV_V0_REGNUM) % LMUL
+ != 0) /* According to RISC-V Vector ABI,
+ first register number should be
+ a multiple of LMUL */
+ {
+ cur_first -= (cur_first - RISCV_V0_REGNUM) % LMUL;
+ cur_first += LMUL;
+ }
+
+ if ((cur_last - cur_first + 1) >= count)
+ {
+ if (cur_first > cur_interval->first)
+ {
+ available_intervals.insert (
+ cur_interval, { cur_interval->first, cur_first - 1 });
+ }
+
+ cur_interval->first = cur_first + count;
+
+ return cur_first;
+ }
+ }
+
+ return -1;
+ }
+
+ int
+ try_use_v0 ()
+ {
+ if (is_v0_used)
+ return get_interval_start (1);
+ else
+ {
+ is_v0_used = true;
+ return RISCV_V0_REGNUM;
+ }
+ }
+
+ bool is_v0_used = false;
+
+ std::list<riscv_vector_arg_reg_interval> available_intervals;
+
+ /* From RISCV-ABI, Standard Vector Calling Convention Variant:
+ The rules for passing vector arguments are as follows:
+ 1. For the first vector mask argument, use v0 to pass it.
+ 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 and
+ are replaced in the 12 argument list with the address.
+ 3. For tuple vector data 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 and are
+ replaced in the argument list with the address
+ */
+};
+
/* Arguments can be passed as on stack arguments, or by reference. The
on stack arguments must be in a continuous region starting from $sp,
while the by reference arguments can be anywhere, but we'll put them
@@ -2757,11 +2896,17 @@ struct riscv_memory_offsets
struct riscv_call_info
{
riscv_call_info (struct gdbarch *gdbarch)
- : int_regs (RISCV_A0_REGNUM, RISCV_A0_REGNUM + 7),
- float_regs (RISCV_FA0_REGNUM, RISCV_FA0_REGNUM + 7)
+ : int_regs (RISCV_A0_REGNUM, RISCV_A0_REGNUM + 7),
+ float_regs (RISCV_FA0_REGNUM, RISCV_FA0_REGNUM + 7),
+ vector_regs (RISCV_V0_REGNUM + 8, RISCV_V0_REGNUM + 23)
{
xlen = riscv_abi_xlen (gdbarch);
flen = riscv_abi_flen (gdbarch);
+ /* According to the RVV specification, a binary file does not require
+ any particular vlenb value. However, a target vlenb value is
+ required to place arguments correctly. For this purpose, we save
+ the riscv_isa_vlenb value here. */
+ vlenb = riscv_isa_vlenb (gdbarch);
/* Reduce the number of integer argument registers when using the
embedded abi (i.e. rv32e). */
@@ -2771,6 +2916,10 @@ struct riscv_call_info
/* Disable use of floating point registers if needed. */
if (!riscv_has_fp_abi (gdbarch))
float_regs.next_regnum = float_regs.last_regnum + 1;
+
+ /* Disable use of vector registers if needed. */
+ if (!riscv_has_vector_abi (gdbarch))
+ vector_regs.available_intervals.clear ();
}
/* Track the memory areas used for holding in-memory arguments to a
@@ -2785,10 +2934,15 @@ struct riscv_call_info
passing an argument. */
struct riscv_arg_reg float_regs;
+ /* Holds information about the next vector register to use for
+ passing an argument. */
+ struct riscv_vector_arg_reg vector_regs;
+
/* The XLEN and FLEN are copied in to this structure for convenience, and
are just the results of calling RISCV_ABI_XLEN and RISCV_ABI_FLEN. */
int xlen;
int flen;
+ int vlenb;
};
/* Return the number of registers available for use as parameters in the
@@ -2830,6 +2984,291 @@ riscv_assign_reg_location (struct riscv_arg_info::location *loc,
return false;
}
+struct rvv_type_info
+{
+ /* Selected Element Width. For RVV_BOOL types, this field holds EEW/EMUL
+ value, encoded into the type. */
+ unsigned sew = 0;
+
+ /* Length Multiplier, amount of registers used for this type. */
+ unsigned lmul = 0;
+
+ /* For non-tuple types, nfield = 1. */
+ unsigned nfield = 0;
+
+ enum class rvv_elem_type : char
+ {
+ RVV_INT,
+ RVV_UINT,
+ RVV_FLOAT,
+ RVV_BOOL,
+ RVV_UNKNOWN,
+ } element_type
+ = rvv_elem_type::RVV_UNKNOWN;
+
+ /* is_fractional_lmul means, that used only part of a vector
+ register. For example, if lmul = 2 and is_fractional_lmul =
+ true it means that used a half of a register. */
+ bool is_fractional_lmul = false;
+};
+
+/* verify_rvv_type() only checks those unavailable configurations.
+ Unavailable configurations:
+ 1) lmul == 1 && is_fractional_lmul == true
+ 2) RVV_FLOAT && sew == 8
+ 3) (sew == 1,2,4) && !RVV_BOOL
+ 4) (lmul != 1 || nfield != 1) && RVV_BOOL */
+static bool
+verify_rvv_type (rvv_type_info type_info)
+{
+ if (type_info.lmul == 1 && type_info.is_fractional_lmul)
+ return false;
+
+ switch (type_info.element_type)
+ {
+ case rvv_type_info::rvv_elem_type::RVV_FLOAT:
+ if (type_info.sew == 8)
+ return false;
+ [[fallthrough]];
+
+ case rvv_type_info::rvv_elem_type::RVV_INT:
+ case rvv_type_info::rvv_elem_type::RVV_UINT:
+ return (type_info.sew != 1 && type_info.sew != 2 && type_info.sew != 4);
+
+ case rvv_type_info::rvv_elem_type::RVV_BOOL:
+ return (type_info.lmul == 1 && type_info.nfield == 1);
+
+ default:
+ return false;
+ }
+
+ gdb_assert_not_reached ("Uknown element type: %c",
+ static_cast<char> (type_info.element_type));
+}
+
+static std::optional<rvv_type_info>
+get_rvv_type_info_unverified (struct type *type)
+{
+ if (!type)
+ return std::nullopt;
+
+ type = check_typedef (type);
+
+ if (!type->name ())
+ {
+ riscv_infcall_debug_printf (
+ "The type name is missing, unable to get RVV type info.");
+ return std::nullopt;
+ }
+
+ if (type->code () != TYPE_CODE_ARRAY && type->code () != TYPE_CODE_STRUCT)
+ {
+ riscv_infcall_debug_printf ("Incorrect type_code: %d. Expected %d "
+ "(TYPE_CODE_ARRAY) or %d (TYPE_CODE_STRUCT)",
+ type->code (), TYPE_CODE_ARRAY,
+ TYPE_CODE_STRUCT);
+ return std::nullopt;
+ }
+
+ struct rvv_type_name_mapping_t
+ {
+ const char *name;
+ rvv_type_info::rvv_elem_type type;
+ };
+ constexpr std::array<rvv_type_name_mapping_t, 4> rvv_type_names = {
+ rvv_type_name_mapping_t{ "int", rvv_type_info::rvv_elem_type::RVV_INT },
+ rvv_type_name_mapping_t{ "uint", rvv_type_info::rvv_elem_type::RVV_UINT },
+ rvv_type_name_mapping_t{ "float",
+ rvv_type_info::rvv_elem_type::RVV_FLOAT },
+ rvv_type_name_mapping_t{ "bool", rvv_type_info::rvv_elem_type::RVV_BOOL }
+ };
+ std::string regex_string ("__rvv_(");
+ std::string separator = "";
+ for (auto type_name : rvv_type_names)
+ {
+ regex_string += separator + type_name.name;
+ separator = "|";
+ }
+ regex_string += ")(1|2|4|8|16|32|64)((m|mf)([1248]))?(x([2-8]))?_t";
+
+ std::regex rvv_type_regex (regex_string);
+ std::cmatch matches;
+ if (!std::regex_search (type->name (), matches, rvv_type_regex))
+ {
+ riscv_infcall_debug_printf ("Failed to match RVV type name: %s",
+ type->name ());
+ return std::nullopt;
+ }
+ /* 'matches' content example for name __rvv_int32mf2x4_t:
+ ["__rvv_vint32mf2x4_t", "int", "32", "mf2", "mf", "2", "x4", "4"]
+ 'matches' content example for name __rvv_bool8_t:
+ ["__rvv_bool8_t", "bool", "8", "", "", "", "", ""] */
+
+ std::string_view parsed_name (matches[1].first, matches[1].length ());
+ std::string_view parsed_sew (matches[2].first, matches[2].length ());
+ std::string_view parsed_lmul_str (matches[3].first, matches[3].length ());
+ std::string_view parsed_lmul_type (matches[4].first, matches[4].length ());
+ std::string_view parsed_lmul_val (matches[5].first, matches[5].length ());
+ std::string_view parsed_nfield_str (matches[6].first, matches[6].length ());
+ std::string_view parsed_nfield_val (matches[7].first, matches[7].length ());
+
+ rvv_type_info res;
+
+ auto it = std::find_if (
+ rvv_type_names.begin (), rvv_type_names.end (),
+ [parsed_name] (const rvv_type_name_mapping_t &type_name) {
+ return parsed_name.compare (type_name.name) == 0;
+ });
+ if (it == rvv_type_names.end ())
+ {
+ riscv_infcall_debug_printf (
+ "Unable to find RVV element type in type name %s.", type->name ());
+ return std::nullopt;
+ }
+ res.element_type = it->type;
+
+ if (std::from_chars (parsed_sew.data (),
+ parsed_sew.data () + parsed_sew.size (), res.sew)
+ .ec
+ != std::errc{})
+ {
+ riscv_infcall_debug_printf (
+ "Failed to convert SEW string (%s) to unsigned", parsed_sew.data ());
+ return std::nullopt;
+ }
+
+ if (!parsed_lmul_str.empty ())
+ {
+ if (parsed_lmul_type.compare ("mf") == 0)
+ res.is_fractional_lmul = true;
+ else
+ res.is_fractional_lmul = false;
+
+ if (std::from_chars (parsed_lmul_val.data (),
+ parsed_lmul_val.data () + parsed_lmul_val.size (),
+ res.lmul)
+ .ec
+ != std::errc{})
+ {
+ riscv_infcall_debug_printf (
+ "Failed to convert LMUL string (%s) to unsigned",
+ parsed_lmul_val.data ());
+ return std::nullopt;
+ }
+ }
+ else
+ res.lmul = 1;
+
+ if (!parsed_nfield_str.empty ())
+ {
+ if (std::from_chars (parsed_nfield_val.data (),
+ parsed_nfield_val.data ()
+ + parsed_nfield_val.size (),
+ res.nfield)
+ .ec
+ != std::errc{})
+ {
+ riscv_infcall_debug_printf (
+ "Failed to convert NFIELD string (%s) to unsigned",
+ parsed_nfield_val.data ());
+ return std::nullopt;
+ }
+ }
+ else
+ res.nfield = 1;
+
+ return res;
+}
+
+static std::optional<rvv_type_info>
+get_rvv_type_info (struct type *type)
+{
+ std::optional<rvv_type_info> res = get_rvv_type_info_unverified (type);
+
+ if (res.has_value () && verify_rvv_type (res.value ()))
+ return res;
+
+ return std::nullopt;
+}
+
+static bool
+is_rvv_type (struct type *type)
+{
+ return get_rvv_type_info (type).has_value ();
+}
+
+static bool
+riscv_assign_vec_reg_location (struct riscv_arg_info *ainfo,
+ struct riscv_vector_arg_reg *reg,
+ struct type *func_arg_type, int vlenb)
+{
+ int data_length = vlenb; /* Amount of data that is stored on one register */
+
+ struct riscv_arg_info::location *loc0 = &ainfo->argloc[0];
+ struct riscv_arg_info::location *loc1 = &ainfo->argloc[1];
+
+ rvv_type_info arg_type_info;
+ if (std::optional<rvv_type_info> res = get_rvv_type_info (func_arg_type))
+ arg_type_info = res.value ();
+ else
+ gdb_assert_not_reached ("Incorrect type %s", func_arg_type->name ());
+
+ riscv_infcall_debug_printf (
+ "rvv_type_info of %s: element_type = %d, sew = %u, lmul = %u (%s "
+ "fractional), nfield = %u",
+ func_arg_type->name (), static_cast<int> (arg_type_info.element_type),
+ arg_type_info.sew, arg_type_info.lmul,
+ (arg_type_info.is_fractional_lmul ? "is" : "is not"),
+ arg_type_info.nfield);
+
+ int num_required_regs
+ = ((arg_type_info.is_fractional_lmul) ? 1 : arg_type_info.lmul)
+ * arg_type_info.nfield;
+
+ if (arg_type_info.is_fractional_lmul)
+ {
+ num_required_regs = arg_type_info.nfield;
+ data_length = data_length / arg_type_info.lmul;
+ }
+
+ int first_regnum = -1;
+
+ /* Representation of vector segmented types, like vint32m4x2_t,
+ is different in Clang and GCC.
+ In Clang, all types are arrays.
+ In GCC, segmented types are struct's, other vector types are arrays. */
+ if (ainfo->type->code () == TYPE_CODE_ARRAY)
+ {
+ if (ainfo->type->target_type ()->code () == TYPE_CODE_BOOL)
+ first_regnum = reg->try_use_v0 ();
+ else
+ first_regnum = reg->get_interval_start (num_required_regs,
+ arg_type_info.nfield);
+ }
+ else if (ainfo->type->code () == TYPE_CODE_STRUCT)
+ {
+ first_regnum
+ = reg->get_interval_start (num_required_regs, arg_type_info.nfield);
+ }
+
+ if (first_regnum == -1)
+ return false;
+
+ int last_regnum = first_regnum + num_required_regs - 1;
+
+ loc0->loc_type = riscv_arg_info::location::in_several_regs;
+ loc0->loc_data.regno = first_regnum;
+ loc0->c_length = data_length;
+ loc0->c_offset = 0;
+
+ loc1->loc_type = riscv_arg_info::location::in_several_regs;
+ loc1->loc_data.regno = last_regnum;
+ loc1->c_length = 0;
+ loc1->c_offset = 0;
+
+ return true;
+}
+
/* Assign LOC a location as the next stack parameter, and update MEMORY to
record that an area of stack has been used to hold the parameter
described by LOC.
@@ -3238,21 +3677,44 @@ riscv_call_arg_struct (struct riscv_arg_info *ainfo,
riscv_call_arg_scalar_int (ainfo, cinfo);
}
+static void
+riscv_call_arg_vector (struct riscv_arg_info *ainfo,
+ struct riscv_call_info *cinfo,
+ struct type *func_arg_type)
+{
+ if (!riscv_assign_vec_reg_location (ainfo, &cinfo->vector_regs,
+ func_arg_type, cinfo->vlenb))
+ {
+ // Try to pass value by reference
+ ainfo->argloc[0].loc_type = riscv_arg_info::location::by_ref;
+ cinfo->memory.ref_offset
+ = align_up (cinfo->memory.ref_offset, ainfo->align);
+ ainfo->argloc[0].loc_data.offset = cinfo->memory.ref_offset;
+ cinfo->memory.ref_offset += ainfo->length;
+ ainfo->argloc[0].c_length = ainfo->length;
+
+ if (!riscv_assign_reg_location (&ainfo->argloc[1], &cinfo->int_regs,
+ cinfo->xlen, 0))
+ riscv_assign_stack_location (&ainfo->argloc[1], &cinfo->memory,
+ cinfo->xlen, cinfo->xlen);
+ }
+}
+
/* Assign a location to call (or return) argument AINFO, the location is
selected from CINFO which holds information about what call argument
locations are available for use next. The TYPE is the type of the
argument being passed, this information is recorded into AINFO (along
with some additional information derived from the type). IS_UNNAMED
is true if this is an unnamed (stdarg) argument, this info is also
- recorded into AINFO.
+ recorded into AINFO. FUNC_ARG_TYPE is the type of the function parameter in
+ its declaration.
After assigning a location to AINFO, CINFO will have been updated. */
static void
-riscv_arg_location (struct gdbarch *gdbarch,
- struct riscv_arg_info *ainfo,
- struct riscv_call_info *cinfo,
- struct type *type, bool is_unnamed)
+riscv_arg_location (struct gdbarch *gdbarch, struct riscv_arg_info *ainfo,
+ struct riscv_call_info *cinfo, struct type *type,
+ struct type *func_arg_type, bool is_unnamed)
{
ainfo->type = type;
ainfo->length = ainfo->type->length ();
@@ -3262,6 +3724,12 @@ riscv_arg_location (struct gdbarch *gdbarch,
ainfo->argloc[0].c_length = 0;
ainfo->argloc[1].c_length = 0;
+ if (is_rvv_type (func_arg_type)) /* for RISC_V vector registers */
+ {
+ riscv_call_arg_vector (ainfo, cinfo, func_arg_type);
+ return;
+ }
+
switch (ainfo->type->code ())
{
case TYPE_CODE_INT:
@@ -3384,6 +3852,13 @@ riscv_print_arg_location (ui_file *stream, struct gdbarch *gdbarch,
}
break;
+ case riscv_arg_info::location::in_several_regs:
+ gdb_printf (
+ stream, ", registers from %s to %s",
+ gdbarch_register_name (gdbarch, info->argloc[0].loc_data.regno),
+ gdbarch_register_name (gdbarch, info->argloc[1].loc_data.regno));
+ break;
+
default:
gdb_assert_not_reached ("unknown argument location type");
}
@@ -3398,15 +3873,13 @@ static void
riscv_regcache_cooked_write (int regnum, const gdb_byte *data, int len,
struct regcache *regcache, int flen)
{
- gdb_byte tmp [sizeof (ULONGEST)];
-
+ int regsize = register_size (regcache->arch (), regnum);
+ std::vector<gdb_byte> tmp (regsize);
/* FP values in FP registers must be NaN-boxed. */
if (riscv_is_fp_regno_p (regnum) && len < flen)
- memset (tmp, -1, sizeof (tmp));
- else
- memset (tmp, 0, sizeof (tmp));
- memcpy (tmp, data, len);
- regcache->cooked_write (regnum, tmp);
+ memset (tmp.data (), -1, tmp.size ());
+ memcpy (tmp.data (), data, len);
+ regcache->cooked_write (regnum, tmp.data ());
}
/* Implement the push dummy call gdbarch callback. */
@@ -3446,16 +3919,21 @@ riscv_push_dummy_call (struct gdbarch *gdbarch,
{
struct value *arg_value;
struct type *arg_type;
+ struct type *func_arg_type;
struct riscv_arg_info *info = &arg_info[i];
arg_value = args[i];
arg_type = check_typedef (arg_value->type ());
- riscv_arg_location (gdbarch, info, &call_info, arg_type,
+ func_arg_type
+ = (i < ftype->num_fields ()) ? ftype->field (i).type () : nullptr;
+
+ riscv_arg_location (gdbarch, info, &call_info, arg_type, func_arg_type,
ftype->has_varargs () && i >= ftype->num_fields ());
if (info->type != arg_type)
arg_value = value_cast (info->type, arg_value);
+
info->contents = arg_value->contents ().data ();
}
@@ -3466,14 +3944,18 @@ riscv_push_dummy_call (struct gdbarch *gdbarch,
if (riscv_debug_infcall)
{
RISCV_INFCALL_SCOPED_DEBUG_START_END ("dummy call args");
- riscv_infcall_debug_printf ("floating point ABI %s in use",
- (riscv_has_fp_abi (gdbarch)
- ? "is" : "is not"));
+ riscv_infcall_debug_printf (
+ "floating point ABI %s in use",
+ (riscv_has_fp_abi (gdbarch) ? "is" : "is not"));
+ riscv_infcall_debug_printf (
+ "vector ABI %s in use",
+ (riscv_has_vector_abi (gdbarch) ? "is" : "is not"));
riscv_infcall_debug_printf ("xlen: %d", call_info.xlen);
riscv_infcall_debug_printf ("flen: %d", call_info.flen);
+ riscv_infcall_debug_printf ("vlenb: %d", call_info.vlenb);
if (return_method == return_method_struct)
- riscv_infcall_debug_printf
- ("[**] struct return pointer in register $A0");
+ riscv_infcall_debug_printf (
+ "[**] struct return pointer in register $A0");
for (i = 0; i < nargs; ++i)
{
struct riscv_arg_info *info = &arg_info [i];
@@ -3547,6 +4029,22 @@ riscv_push_dummy_call (struct gdbarch *gdbarch,
second_arg_data = (gdb_byte *) &dst;
break;
+ case riscv_arg_info::location::in_several_regs:
+ {
+ const gdb_byte *cur_contents = info->contents;
+ int cur_c_length = info->argloc[0].c_length;
+ for (int cur_regno = info->argloc[0].loc_data.regno;
+ cur_regno <= info->argloc[1].loc_data.regno; cur_regno++)
+ {
+ riscv_regcache_cooked_write (cur_regno, cur_contents,
+ cur_c_length, regcache,
+ call_info.flen);
+ cur_contents += cur_c_length;
+ }
+ second_arg_length = 0;
+ }
+ break;
+
default:
gdb_assert_not_reached ("unknown argument location type");
}
@@ -3577,6 +4075,8 @@ riscv_push_dummy_call (struct gdbarch *gdbarch,
}
case riscv_arg_info::location::by_ref:
+ case riscv_arg_info::location::
+ in_several_regs: /* We shouldn't get here for this case*/
default:
/* The second location should never be a reference, any
argument being passed by reference just places its address
@@ -3615,9 +4115,14 @@ riscv_return_value (struct gdbarch *gdbarch,
struct riscv_call_info call_info (gdbarch);
struct riscv_arg_info info;
struct type *arg_type;
+ struct type *func_retval_type;
arg_type = check_typedef (type);
- riscv_arg_location (gdbarch, &info, &call_info, arg_type, false);
+ func_retval_type = (function && function->type ())
+ ? function->type ()->target_type ()
+ : nullptr;
+ riscv_arg_location (gdbarch, &info, &call_info, arg_type, func_retval_type,
+ false);
if (riscv_debug_infcall)
{
@@ -3770,9 +4275,41 @@ riscv_return_value (struct gdbarch *gdbarch,
}
break;
+ case riscv_arg_info::location::in_several_regs:
+ {
+ int first_regnum = info.argloc[0].loc_data.regno;
+ int last_regnum = info.argloc[1].loc_data.regno;
+
+ gdb_byte *tmp_readbuf = readbuf;
+ const gdb_byte *tmp_writebuf = writebuf;
+
+ for (int cur_regnum = first_regnum; cur_regnum <= last_regnum;
+ cur_regnum++)
+ {
+ if (readbuf)
+ {
+ gdb_byte *ptr = tmp_readbuf + info.argloc[0].c_offset;
+ regcache->cooked_read_part (cur_regnum, 0,
+ info.argloc[0].c_length, ptr);
+ tmp_readbuf += info.argloc[0].c_length;
+ }
+
+ if (writebuf)
+ {
+ const gdb_byte *ptr
+ = tmp_writebuf + info.argloc[0].c_offset;
+ riscv_regcache_cooked_write (cur_regnum, ptr,
+ info.argloc[0].c_length,
+ regcache, call_info.flen);
+ tmp_writebuf += info.argloc[0].c_length;
+ }
+ }
+ }
+ break;
+
case riscv_arg_info::location::on_stack:
default:
- error (_("invalid argument location"));
+ error (_ ("invalid argument location"));
break;
}
@@ -3804,6 +4341,7 @@ riscv_return_value (struct gdbarch *gdbarch,
switch (info.argloc[0].loc_type)
{
case riscv_arg_info::location::in_reg:
+ case riscv_arg_info::location::in_several_regs:
return RETURN_VALUE_REGISTER_CONVENTION;
case riscv_arg_info::location::by_ref:
return RETURN_VALUE_ABI_PRESERVES_ADDRESS;
@@ -3973,6 +4511,28 @@ riscv_features_from_bfd (const bfd *abfd)
}
features.embedded = true;
}
+
+ obj_attribute *obj_attr = elf_known_obj_attributes_proc (abfd);
+ const char *march = obj_attr[Tag_RISCV_arch].s;
+ if (march)
+ {
+ /* According to the RVV specification, a binary file does not require
+ any particular vlenb value. Therefore, we used minimal vlenb value
+ to indicate that the vector ABI is in use. Additionally, a valid
+ vlenb value is required here, as it will be used later to create a
+ default target description. */
+ std::string march_str (march);
+ if (march_str.find ("_v") != std::string::npos)
+ features.vlenb = 16;
+ else if (march_str.find ("_zve64") != std::string::npos)
+ features.vlenb = 8;
+ else if (march_str.find ("_zve32") != std::string::npos)
+ features.vlenb = 4;
+ else
+ features.vlenb = 0;
+ }
+ else
+ features.vlenb = 0;
}
return features;
@@ -4266,18 +4826,24 @@ riscv_gdbarch_init (struct gdbarch_info info,
however, this has not been tested in GDB yet, so for now we require
that the requested xlen match the targets xlen. */
if (abi_features.xlen != features.xlen)
- error (_("bfd requires xlen %d, but target has xlen %d"),
- abi_features.xlen, features.xlen);
+ error (_ ("bfd requires xlen %d, but target has xlen %d"),
+ abi_features.xlen, features.xlen);
/* We do support running binaries compiled for 32-bit float on targets
with 64-bit float, so we only complain if the binary requires more
than the target has available. */
if (abi_features.flen > features.flen)
- error (_("bfd requires flen %d, but target has flen %d"),
- abi_features.flen, features.flen);
+ error (_ ("bfd requires flen %d, but target has flen %d"),
+ abi_features.flen, features.flen);
+
+ /* Look at riscv_features_from_bfd*/
+ if ((abi_features.vlenb > 0) && (features.vlenb == 0))
+ {
+ warning (_ ("bfd requires non-zero vlenb, but target has vlenb = 0, "
+ "vector registers unsupported"));
+ }
/* Find a candidate among the list of pre-declared architectures. */
- for (arches = gdbarch_list_lookup_by_info (arches, &info);
- arches != NULL;
+ for (arches = gdbarch_list_lookup_by_info (arches, &info); arches != NULL;
arches = gdbarch_list_lookup_by_info (arches->next, &info))
{
/* Check that the feature set of the ARCHES matches the feature set
@@ -5388,3 +5954,13 @@ riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
return 0;
}
+
+bool
+riscv_is_vpr_or_vcsr (unsigned regnum)
+{
+ return (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V0_REGNUM + 31)
+ || regnum == RISCV_CSR_VSTART_REGNUM
+ || regnum == RISCV_CSR_VCSR_REGNUM || regnum == RISCV_CSR_VL_REGNUM
+ || regnum == RISCV_CSR_VTYPE_REGNUM
+ || regnum == RISCV_CSR_VLENB_REGNUM;
+}
diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
index 2903aefd007..91dd01dac62 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -23,61 +23,7 @@
#include "arch/riscv.h"
#include "gdbarch.h"
-
-/* RiscV register numbers. */
-enum
-{
- RISCV_ZERO_REGNUM = 0, /* Read-only register, always 0. */
- RISCV_RA_REGNUM = 1, /* Return Address. */
- RISCV_SP_REGNUM = 2, /* Stack Pointer. */
- RISCV_GP_REGNUM = 3, /* Global Pointer. */
- RISCV_TP_REGNUM = 4, /* Thread Pointer. */
- RISCV_FP_REGNUM = 8, /* Frame Pointer. */
- RISCV_A0_REGNUM = 10, /* First argument. */
- RISCV_A1_REGNUM = 11, /* Second argument. */
- RISCV_A2_REGNUM = 12, /* Third argument. */
- RISCV_A3_REGNUM = 13, /* Forth argument. */
- RISCV_A4_REGNUM = 14, /* Fifth argument. */
- RISCV_A5_REGNUM = 15, /* Sixth argument. */
- RISCV_A7_REGNUM = 17, /* Register to pass syscall number. */
- RISCV_PC_REGNUM = 32, /* Program Counter. */
-
- RISCV_NUM_INTEGER_REGS = 32,
-
- RISCV_FIRST_FP_REGNUM = 33, /* First Floating Point Register */
- RISCV_FA0_REGNUM = 43,
- RISCV_FA1_REGNUM = RISCV_FA0_REGNUM + 1,
- RISCV_LAST_FP_REGNUM = 64, /* Last Floating Point Register */
-
- RISCV_FIRST_CSR_REGNUM = 65, /* First CSR */
-#define DECLARE_CSR(name, num, class, define_version, abort_version) \
- RISCV_ ## num ## _REGNUM = RISCV_FIRST_CSR_REGNUM + num,
-#include "opcode/riscv-opc.h"
-#undef DECLARE_CSR
- RISCV_LAST_CSR_REGNUM = 4160,
- RISCV_CSR_LEGACY_MISA_REGNUM = 0xf10 + RISCV_FIRST_CSR_REGNUM,
-
- RISCV_PRIV_REGNUM = 4161,
-
- RISCV_V0_REGNUM,
-
- RISCV_V31_REGNUM = RISCV_V0_REGNUM + 31,
-
- RISCV_LAST_REGNUM = RISCV_V31_REGNUM
-};
-
-/* RiscV DWARF register numbers. */
-enum
-{
- RISCV_DWARF_REGNUM_X0 = 0,
- RISCV_DWARF_REGNUM_X31 = 31,
- RISCV_DWARF_REGNUM_F0 = 32,
- RISCV_DWARF_REGNUM_F31 = 63,
- RISCV_DWARF_REGNUM_V0 = 96,
- RISCV_DWARF_REGNUM_V31 = 127,
- RISCV_DWARF_FIRST_CSR = 4096,
- RISCV_DWARF_LAST_CSR = 8191,
-};
+#include "riscv-regs.h"
/* RISC-V specific per-architecture information. */
struct riscv_gdbarch_tdep : gdbarch_tdep_base
@@ -135,6 +81,8 @@ extern int riscv_isa_xlen (struct gdbarch *gdbarch);
single, double or quad floating point support is available. */
extern int riscv_isa_flen (struct gdbarch *gdbarch);
+extern int riscv_isa_vlenb (struct gdbarch *gdbarch);
+
/* Return the width in bytes of the general purpose register abi for
GDBARCH. This can be equal to, or less than RISCV_ISA_XLEN and reflects
how the binary was compiled rather than the hardware that is available.
@@ -158,6 +106,8 @@ extern int riscv_abi_flen (struct gdbarch *gdbarch);
argument registers. */
extern bool riscv_abi_embedded (struct gdbarch *gdbarch);
+extern int riscv_abi_vlenb (struct gdbarch *gdbarch);
+
/* Single step based on where the current instruction will take us. */
extern std::vector<CORE_ADDR> riscv_software_single_step
(struct regcache *regcache);
@@ -194,4 +144,7 @@ extern int riscv_process_record (struct gdbarch *gdbarch,
/* The names of the RISC-V target description features. */
extern const char *riscv_feature_name_csr;
+/* Determines if regnum corresponds to vector register vX or vector CSR. */
+extern bool riscv_is_vpr_or_vcsr (unsigned regnum);
+
#endif /* GDB_RISCV_TDEP_H */
diff --git a/gdbserver/linux-riscv-low.cc b/gdbserver/linux-riscv-low.cc
index f70ed597051..82362542c19 100644
--- a/gdbserver/linux-riscv-low.cc
+++ b/gdbserver/linux-riscv-low.cc
@@ -21,6 +21,7 @@
#include "linux-low.h"
#include "tdesc.h"
#include "elf/common.h"
+#include "nat/riscv-linux-ptrace.h"
#include "nat/riscv-linux-tdesc.h"
#include "opcode/riscv.h"
@@ -102,26 +103,6 @@ riscv_target::low_get_syscall_trapinfo (regcache *regcache, int *sysno)
*sysno = (int)l_sysno;
}
-/* Implementation of linux target ops method "low_arch_setup". */
-
-void
-riscv_target::low_arch_setup ()
-{
- static const char *expedite_regs[] = { "sp", "pc", NULL };
-
- const riscv_gdbarch_features features
- = riscv_linux_read_features (current_thread->id.lwp ());
- target_desc_up tdesc = riscv_create_target_description (features);
-
- if (tdesc->expedite_regs.empty ())
- {
- init_target_desc (tdesc.get (), expedite_regs, GDB_OSABI_LINUX);
- gdb_assert (!tdesc->expedite_regs.empty ());
- }
-
- current_process ()->tdesc = tdesc.release ();
-}
-
/* Collect GPRs from REGCACHE into BUF. */
static void
@@ -185,25 +166,76 @@ riscv_store_fpregset (struct regcache *regcache, const void *buf)
supply_register_by_name (regcache, "fcsr", regbuf);
}
+/* Collect vector regs from REGCACHE into BUF. */
+
+static void
+riscv_fill_vecregset (struct regcache *regcache, void *buf)
+{
+ const struct target_desc *tdesc = regcache->tdesc;
+ int v0_regno = find_regno (tdesc, "v0");
+
+ struct __riscv_v_regset_state *vecregs
+ = (struct __riscv_v_regset_state *)buf;
+ unsigned long vlenb = vecregs->vlenb;
+ gdb_byte *regbuf = (gdb_byte *)vecregs->vreg;
+
+ for (int i = 0; i < 32; i++, regbuf += vlenb)
+ collect_register (regcache, v0_regno + i, regbuf);
+
+ collect_register_by_name (regcache, "vstart", &vecregs->vstart);
+ collect_register_by_name (regcache, "vcsr", &vecregs->vcsr);
+ collect_register_by_name (regcache, "vl", &vecregs->vl);
+ collect_register_by_name (regcache, "vtype", &vecregs->vtype);
+ collect_register_by_name (regcache, "vlenb", &vecregs->vlenb);
+}
+
+/* Supply vector regs from BUF into REGCACHE. */
+
+static void
+riscv_store_vecregset (struct regcache *regcache, const void *buf)
+{
+ const struct target_desc *tdesc = regcache->tdesc;
+ int v0_regno = find_regno (tdesc, "v0");
+
+ const struct __riscv_v_regset_state *vecregs
+ = (const struct __riscv_v_regset_state *)buf;
+ if (vecregs->vlenb == 0)
+ return;
+ int v0_regsize = register_size (tdesc, v0_regno);
+ gdb_assert (vecregs->vlenb == v0_regsize);
+ unsigned long vlenb = vecregs->vlenb;
+ const gdb_byte *regbuf = (const gdb_byte *)vecregs->vreg;
+
+ for (int i = 0; i < 32; i++, regbuf += vlenb)
+ supply_register (regcache, v0_regno + i, regbuf);
+
+ supply_register_by_name (regcache, "vstart", &vecregs->vstart);
+ supply_register_by_name (regcache, "vcsr", &vecregs->vcsr);
+ supply_register_by_name (regcache, "vl", &vecregs->vl);
+ supply_register_by_name (regcache, "vtype", &vecregs->vtype);
+ supply_register_by_name (regcache, "vlenb", &vecregs->vlenb);
+}
+
/* RISC-V/Linux regsets. FPRs are optional and come in different sizes,
so define multiple regsets for them marking them all as OPTIONAL_REGS
rather than FP_REGS, so that "regsets_fetch_inferior_registers" picks
the right one according to size. */
-static struct regset_info riscv_regsets[] = {
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
- sizeof (elf_gregset_t), GENERAL_REGS,
- riscv_fill_gregset, riscv_store_gregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
- sizeof (struct __riscv_mc_q_ext_state), OPTIONAL_REGS,
- riscv_fill_fpregset, riscv_store_fpregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
- sizeof (struct __riscv_mc_d_ext_state), OPTIONAL_REGS,
- riscv_fill_fpregset, riscv_store_fpregset },
- { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
- sizeof (struct __riscv_mc_f_ext_state), OPTIONAL_REGS,
- riscv_fill_fpregset, riscv_store_fpregset },
- NULL_REGSET
-};
+static struct regset_info riscv_regsets[]
+ = { { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
+ sizeof (elf_gregset_t), GENERAL_REGS, riscv_fill_gregset,
+ riscv_store_gregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
+ sizeof (struct __riscv_mc_q_ext_state), OPTIONAL_REGS,
+ riscv_fill_fpregset, riscv_store_fpregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
+ sizeof (struct __riscv_mc_d_ext_state), OPTIONAL_REGS,
+ riscv_fill_fpregset, riscv_store_fpregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
+ sizeof (struct __riscv_mc_f_ext_state), OPTIONAL_REGS,
+ riscv_fill_fpregset, riscv_store_fpregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_RISCV_VECTOR, 0,
+ EXTENDED_REGS, riscv_fill_vecregset, riscv_store_vecregset },
+ NULL_REGSET };
/* RISC-V/Linux regset information. */
static struct regsets_info riscv_regsets_info =
@@ -229,6 +261,44 @@ riscv_target::get_regs_info ()
return &riscv_regs;
}
+/* Setup Vector Regset. */
+static void
+setup_vector_regset (struct regset_info *vector_regset_info,
+ const riscv_gdbarch_features *features)
+{
+ int vecreg_size = features->vlenb * 8;
+ vector_regset_info->size
+ = sizeof (struct __riscv_v_regset_state) + 32 * vecreg_size;
+}
+
+/* Implementation of linux target ops method "low_arch_setup". */
+
+void
+riscv_target::low_arch_setup ()
+{
+ static const char *expedite_regs[] = { "sp", "pc", NULL };
+
+ const riscv_gdbarch_features features
+ = riscv_linux_read_features (current_thread->id.lwp ());
+ target_desc_up tdesc = riscv_create_target_description (features);
+
+ struct regset_info *regset;
+ for (regset = riscv_regsets; regset->size >= 0; regset++)
+ if (regset->nt_type == NT_RISCV_VECTOR)
+ {
+ setup_vector_regset (regset, &features);
+ break;
+ }
+
+ if (tdesc->expedite_regs.empty ())
+ {
+ init_target_desc (tdesc.get (), expedite_regs, GDB_OSABI_LINUX);
+ gdb_assert (!tdesc->expedite_regs.empty ());
+ }
+
+ current_process ()->tdesc = tdesc.release ();
+}
+
/* Implementation of linux target ops method "low_fetch_register". */
bool
diff --git a/gdbsupport/common-utils.h b/gdbsupport/common-utils.h
index 10bf9f49e2a..a29aa8edfa4 100644
--- a/gdbsupport/common-utils.h
+++ b/gdbsupport/common-utils.h
@@ -20,12 +20,14 @@
#ifndef GDBSUPPORT_COMMON_UTILS_H
#define GDBSUPPORT_COMMON_UTILS_H
+#include <optional>
#include <string>
#include <vector>
#include "gdbsupport/byte-vector.h"
#include "gdbsupport/gdb_unique_ptr.h"
#include "gdbsupport/array-view.h"
#include "poison.h"
+#include <charconv>
#include <string_view>
#if defined HAVE_LIBXXHASH
diff --git a/include/elf/common.h b/include/elf/common.h
index 5d0f93ebf56..f3bc367b962 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -776,6 +776,7 @@
/* note name must be "LINUX". */
#define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */
/* note name must be "LINUX". */
+#define NT_RISCV_VECTOR 0x901 /* RISC-V Vector Registers. */
#define NT_SIGINFO 0x53494749 /* Fields of siginfo_t. */
#define NT_FILE 0x46494c45 /* Description of mapped files. */
--
2.43.0
^ permalink raw reply [flat|nested] 2+ messages in thread* [PATCH 2/2] RISC-V Vector Extension Support Testing
2025-11-07 16:55 [PATCH 1/2] RISC-V Vector Extension Support Kirill Radkin
@ 2025-11-07 16:55 ` Kirill Radkin
0 siblings, 0 replies; 2+ messages in thread
From: Kirill Radkin @ 2025-11-07 16:55 UTC (permalink / raw)
To: gdb-patches; +Cc: Kirill Radkin
This patch add extensive testing for RISC-V Vector Extension Support.
---
...iscv-vector-abi-full-generate-template.txt | 153 ++++++++
.../riscv-vector-abi-full-generate.py | 360 ++++++++++++++++++
.../gdb.arch/riscv-vector-abi-full.c | 23 ++
.../gdb.arch/riscv-vector-abi-full.exp | 65 ++++
gdb/testsuite/gdb.arch/riscv-vector-abi.c | 157 ++++++++
gdb/testsuite/gdb.arch/riscv-vector-abi.exp | 230 +++++++++++
.../gdb.arch/riscv-vu-availability.c | 67 ++++
.../gdb.arch/riscv-vu-availability.exp | 72 ++++
.../gdb.arch/riscv-vu-consitency-checks.c | 79 ++++
.../gdb.arch/riscv-vu-consitency-checks.exp | 152 ++++++++
gdb/testsuite/gdb.arch/riscv-vu-ctx-print.c | 106 ++++++
gdb/testsuite/gdb.arch/riscv-vu-ctx-print.exp | 107 ++++++
gdb/testsuite/gdb.arch/riscv-vu-printout.c | 69 ++++
gdb/testsuite/gdb.arch/riscv-vu-printout.exp | 92 +++++
.../gdb.arch/riscv-vu-rvv-unsupported.c | 23 ++
.../gdb.arch/riscv-vu-rvv-unsupported.exp | 46 +++
gdb/testsuite/gdb.arch/riscv-vu-rwr.c | 62 +++
gdb/testsuite/gdb.arch/riscv-vu-rwr.exp | 163 ++++++++
.../gdb.arch/riscv-vu-side-effects.c | 86 +++++
.../gdb.arch/riscv-vu-side-effects.exp | 162 ++++++++
gdb/testsuite/lib/riscv64-rvv-lib.exp | 166 ++++++++
21 files changed, 2440 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/riscv-vector-abi-full-generate-template.txt
create mode 100644 gdb/testsuite/gdb.arch/riscv-vector-abi-full-generate.py
create mode 100644 gdb/testsuite/gdb.arch/riscv-vector-abi-full.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vector-abi-full.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-vector-abi.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vector-abi.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-availability.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-availability.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-consitency-checks.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-consitency-checks.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-ctx-print.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-ctx-print.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-printout.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-printout.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-rvv-unsupported.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-rvv-unsupported.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-rwr.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-rwr.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-side-effects.c
create mode 100644 gdb/testsuite/gdb.arch/riscv-vu-side-effects.exp
create mode 100644 gdb/testsuite/lib/riscv64-rvv-lib.exp
diff --git a/gdb/testsuite/gdb.arch/riscv-vector-abi-full-generate-template.txt b/gdb/testsuite/gdb.arch/riscv-vector-abi-full-generate-template.txt
new file mode 100644
index 00000000000..081572bd8ea
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vector-abi-full-generate-template.txt
@@ -0,0 +1,153 @@
+{% macro main_header(file) -%}
+/* DO NOT EDIT: Autogenerated by {{ file }}
+ Copyright 2025 Free Software Foundation, Inc.
+ This file is part of GDB, the GNU debugger. */
+
+#include <riscv_vector.h>
+{% endmacro %}
+
+{% macro main_tail_start() %}
+void
+test ()
+{
+ size_t vl = 0;
+{% endmacro %}
+
+{% macro expect_header(file) -%}
+# DO NOT EDIT: Autogenerated by {{ file }}
+# Copyright 2025 Free Software Foundation, Inc.
+# This file is part of GDB, the GNU debugger.
+
+proc generate_response { start step count } {
+ if {$step == 0 && $count > 8} {
+ return "\\{${start} <repeats ${count} times>\\}"
+ }
+
+ set res "\\{$start"
+ set count [expr {$count - 1}]
+
+ for {set i 0} {$i < $count} {incr i} {
+ set start [expr {$start + $step}]
+ set res "${res}, $start"
+ }
+ set res "${res}\\}"
+ return $res
+}
+
+proc generate_tuple_response { nfields starts step count } {
+ set res "\\{__val = \\{"
+ set start [lindex $starts 0]
+ set entry [generate_response $start $step $count]
+ set res "${res}${entry}"
+
+ for {set i 1} {$i < $nfields} {incr i} {
+ set start [lindex $starts $i]
+ set entry [generate_response $start $step $count]
+ set res "${res}, $entry"
+ }
+
+ set res "${res}\\}\\}"
+ return $res
+}
+
+standard_testfile [standard_output_file riscv-vector-abi-full-generated.c]
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+{% endmacro %}
+
+{% macro func_name_template(type_name) -%}
+add_{{ type_name }}
+{%- endmacro %}
+
+{% macro func_template(type_name, vadd_name, func_name, vsetvlmax) -%}
+{{ type_name }}
+{{ func_name }} ({{ type_name }} a, {{ type_name }} b)
+{
+ size_t vl = {{ vsetvlmax }} ();
+ return {{ vadd_name }} (a, b, vl);
+}
+
+{% endmacro %}
+
+{% macro main_entry_template(type_name, var_idx, vmv_name, var_val, func_name, vsetvlmax) %}
+ // {{ type_name }}
+ vl = {{ vsetvlmax }} ();
+ {{ type_name }} var{{ var_idx }} = {{ vmv_name }} ({{ var_val }}, vl);
+ {{ type_name }} res{{ var_idx }} = {{ func_name }} (var{{ var_idx }}, var{{ var_idx }});
+ // {{ type_name }}_break
+{% endmacro %}
+
+{% macro test_entry_template(main_file, type_name, break_idx, var_idx, var_val, res_val, func_name) %}
+gdb_breakpoint "[host_standard_output_file {{ main_file }}]:[gdb_get_line_number "{{ type_name }}_break"]"
+gdb_continue_to_breakpoint "break {{ break_idx }}"
+set vl [get_valueof "/d" "vl" -1 "get_vl_{{ break_idx }}"]
+gdb_test "print var{{ var_idx }}" "[generate_response {{ var_val }} 0 $vl]"
+gdb_test "print res{{ var_idx }}" "[generate_response {{ res_val }} 0 $vl]"
+gdb_test "print {{ func_name }} (var{{ var_idx }}, var{{ var_idx }})" "[generate_response {{ res_val }} 0 $vl]"
+{% endmacro %}
+
+{% macro tuple_func_template_start() %}
+{type_name}
+{func_name} ({type_name} a, {type_name} b)
+{{ '{{' }}
+ {type_name} result;
+ size_t vl = {vsetvlmax} ();
+{% endmacro %}
+
+{% macro tuple_func_template_entry(index) %}
+ {short_type_name} a{{ index }} = {vget_name} (a, {{ index }});
+ {short_type_name} b{{ index }} = {vget_name} (b, {{ index }});
+ {short_type_name} r{{ index }} = {vadd_name} (a{{ index }}, b{{ index }}, vl);
+ result = {vset_name} (result, {{ index }}, r{{ index }});
+{% endmacro %}
+
+{% macro tuple_func_template_end() %}
+ return result;
+{{ '}}' }}
+{% endmacro %}
+
+{% macro tuple_main_entry_template_start() %}
+ // {type_name}
+ vl = {vsetvlmax} ();
+ {type_name} var{var_idx};
+{% endmacro %}
+
+{% macro tuple_main_entry_template_entry(index) %}
+ {short_type_name} var{var_idx}_{{ index }} = {vmv_name} ({var_values[{{ index }}]}, vl);
+ var{var_idx} = {vset_name} (var{var_idx}, {{ index }}, var{var_idx}_{{ index }});
+{% endmacro %}
+
+{% macro tuple_main_entry_template_end() %}
+ {type_name} res{var_idx} = {func_name} (var{var_idx}, var{var_idx});
+ // {type_name}_break
+{% endmacro %}
+
+{% macro tuple_test_template_start(main_file) %}
+gdb_breakpoint "[host_standard_output_file {{ main_file }}]:[gdb_get_line_number "{type_name}_break"]"
+gdb_continue_to_breakpoint "break {break_idx}"
+set vl [get_valueof "/d" "vl" -1 "get_vl_{break_idx}"]
+{% endmacro %}
+
+{% macro tuple_test_template_entry_first(index) -%}
+gdb_test "print var{var_idx}_{{ index }}" "[generate_response {var_values[{{ index }}]} 0 $vl]"
+{% endmacro %}
+
+{% macro tuple_test_template_entry_middle() -%}
+set res_values {{ '{{' }}
+{%- endmacro %}
+
+{% macro tuple_test_template_entry_second(index) -%}
+{{ ' ' }}{{ '{{' }}{res_values[{{ index }}]}{{ '}}' }}
+{%- endmacro %}
+
+{% macro tuple_test_template_end(nfields) -%}
+{{ ' }}' }}
+gdb_test "print res{var_idx}" "[generate_tuple_response {{ nfields }} $res_values 0 $vl]"
+gdb_test "print {func_name} (var{var_idx}, var{var_idx})" "[generate_tuple_response {{ nfields }} $res_values 0 $vl]"
+{% endmacro %}
diff --git a/gdb/testsuite/gdb.arch/riscv-vector-abi-full-generate.py b/gdb/testsuite/gdb.arch/riscv-vector-abi-full-generate.py
new file mode 100644
index 00000000000..b9113d526df
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vector-abi-full-generate.py
@@ -0,0 +1,360 @@
+import os
+import itertools
+import re
+from pathlib import Path
+from enum import StrEnum
+from jinja2 import Environment, FileSystemLoader
+
+FILE = Path(__file__).name
+TEST_DIR = Path(__file__).resolve().parent
+JINJA_TEMPLATE_FILE = "riscv-vector-abi-full-generate-template.txt"
+
+WORK_DIR = os.getenv("WORK_DIR")
+TEST_NAME = os.getenv("TEST_NAME")
+
+
+class ElemType(StrEnum):
+ INT = "int"
+ UINT = "uint"
+ FLOAT = "float"
+
+
+class InstrType(StrEnum):
+ VADD = "vadd"
+ VMV = "vmv"
+ VGET = "vget"
+ VSET = "vset"
+ VSETVLMAX = "vlmax"
+
+
+class InstructionTemplate:
+ vadd_instr_templates = {
+ ElemType.INT: "__riscv_vadd_vv_i{suffix1}",
+ ElemType.UINT: "__riscv_vadd_vv_u{suffix1}",
+ ElemType.FLOAT: "__riscv_vfadd_vv_f{suffix1}",
+ }
+
+ vmv_instr_templates = {
+ ElemType.INT: "__riscv_vmv_v_x_i{suffix1}",
+ ElemType.UINT: "__riscv_vmv_v_x_u{suffix1}",
+ ElemType.FLOAT: "__riscv_vfmv_v_f_f{suffix1}",
+ }
+
+ vget_instr_templates = {
+ ElemType.INT: "__riscv_vget_v_i{suffix1}_i{suffix2}",
+ ElemType.UINT: "__riscv_vget_v_u{suffix1}_u{suffix2}",
+ ElemType.FLOAT: "__riscv_vget_v_f{suffix1}_f{suffix2}",
+ }
+
+ vset_instr_templates = {
+ ElemType.INT: "__riscv_vset_v_i{suffix1}_i{suffix2}",
+ ElemType.UINT: "__riscv_vset_v_u{suffix1}_u{suffix2}",
+ ElemType.FLOAT: "__riscv_vset_v_f{suffix1}_f{suffix2}",
+ }
+
+ vsetvlmax_template = {k: "__riscv_vsetvlmax_e{suffix1}" for k in ElemType}
+
+ templates = {
+ InstrType.VADD: vadd_instr_templates,
+ InstrType.VMV: vmv_instr_templates,
+ InstrType.VGET: vget_instr_templates,
+ InstrType.VSET: vset_instr_templates,
+ InstrType.VSETVLMAX: vsetvlmax_template,
+ }
+
+ def get(self, elem_type: ElemType, instr_type: InstrType):
+ return self.templates[instr_type][elem_type]
+
+
+def generate(directory: Path, test_name: Path):
+ instr_templates = InstructionTemplate()
+
+ env = Environment(loader=FileSystemLoader(str(TEST_DIR)))
+ tpl = env.get_template(str(JINJA_TEMPLATE_FILE))
+
+ counter_vars = itertools.count(0)
+ counter_values = itertools.cycle(range(0, 64, 1))
+ counter_break_idx = itertools.count(2)
+
+ main_file = Path(f"{test_name}.c")
+ main_file_path = directory / main_file
+
+ test_script = Path(f"{test_name}.exp")
+ test_script_path = directory / test_script
+
+ if not os.path.exists(main_file_path):
+ os.mknod(main_file_path)
+
+ if not os.path.exists(test_script_path):
+ os.mknod(test_script_path)
+
+ main_header = tpl.module.main_header(FILE)
+
+ with open(main_file_path, "w") as f:
+ f.write(main_header)
+
+ main_tail = tpl.module.main_tail_start()
+
+ expect_header = tpl.module.expect_header(FILE)
+
+ with open(test_script_path, "w") as f:
+ f.write(expect_header)
+
+ # int, uint, float
+
+ # fmt: off
+ vint_types = [
+ # 8-bit
+ "vint8mf8_t", "vint8mf4_t", "vint8mf2_t", "vint8m1_t", "vint8m2_t", "vint8m4_t", "vint8m8_t",
+
+ # 16-bit
+ "vint16mf4_t", "vint16mf2_t", "vint16m1_t", "vint16m2_t", "vint16m4_t", "vint16m8_t",
+
+ # 32-bit
+ "vint32mf2_t", "vint32m1_t", "vint32m2_t", "vint32m4_t", "vint32m8_t",
+
+ # 64-bit
+ "vint64m1_t", "vint64m2_t", "vint64m4_t", "vint64m8_t",
+ ]
+
+ vuint_types = [_.replace("int", "uint") for _ in vint_types]
+
+ vfloat_types = [
+ # SEW = 16 (half-precision)
+ "vfloat16mf4_t", "vfloat16mf2_t", "vfloat16m1_t", "vfloat16m2_t", "vfloat16m4_t", "vfloat16m8_t",
+
+ # SEW = 32 (single-precision)
+ "vfloat32mf2_t", "vfloat32m1_t", "vfloat32m2_t", "vfloat32m4_t", "vfloat32m8_t",
+
+ # SEW = 64 (double-precision)
+ "vfloat64m1_t", "vfloat64m2_t", "vfloat64m4_t", "vfloat64m8_t",
+ ]
+ # fmt: on
+
+ for type_name in vint_types + vuint_types + vfloat_types:
+ m = re.match(r"v(int|uint|float)(8|16|32|64)(m|mf)(1|2|4|8)_t", type_name)
+ if not m:
+ raise RuntimeError("wrong type")
+
+ elem_type = ElemType(m.group(1))
+ small_suffix = "".join(m.group(2, 3, 4)) # 16m2
+
+ func_name = tpl.module.func_name_template(type_name)
+ vsetvlmax = instr_templates.get(elem_type, InstrType.VSETVLMAX).format(
+ suffix1=small_suffix
+ )
+ vadd_name = instr_templates.get(elem_type, InstrType.VADD).format(
+ suffix1=small_suffix
+ )
+ vmv_name = instr_templates.get(elem_type, InstrType.VMV).format(
+ suffix1=small_suffix
+ )
+
+ var_idx = next(counter_vars)
+ var_val = next(counter_values)
+ res_val = 2 * var_val
+
+ new_line = tpl.module.func_template(
+ type_name,
+ vadd_name,
+ func_name,
+ vsetvlmax,
+ )
+
+ with open(main_file_path, "a") as f:
+ f.write(new_line)
+
+ main_tail += tpl.module.main_entry_template(
+ type_name,
+ var_idx,
+ vmv_name,
+ var_val,
+ func_name,
+ vsetvlmax,
+ )
+
+ break_idx = next(counter_break_idx)
+ test_command = tpl.module.test_entry_template(
+ main_file,
+ type_name,
+ break_idx,
+ var_idx,
+ var_val,
+ res_val,
+ func_name,
+ )
+ with open(test_script_path, "a") as f:
+ f.write(test_command)
+
+ # tuple int
+
+ # fmt: off
+ vint_tuple_types = [
+ # LMUL = mf8
+ "vint8mf8x2_t", "vint8mf8x3_t", "vint8mf8x4_t", "vint8mf8x5_t", "vint8mf8x6_t", "vint8mf8x7_t", "vint8mf8x8_t",
+
+ # LMUL = mf4
+ "vint8mf4x2_t", "vint8mf4x3_t", "vint8mf4x4_t", "vint8mf4x5_t", "vint8mf4x6_t", "vint8mf4x7_t", "vint8mf4x8_t",
+ "vint16mf4x2_t", "vint16mf4x3_t", "vint16mf4x4_t", "vint16mf4x5_t", "vint16mf4x6_t", "vint16mf4x7_t", "vint16mf4x8_t",
+
+ # LMUL = mf2
+ "vint8mf2x2_t", "vint8mf2x3_t", "vint8mf2x4_t", "vint8mf2x5_t", "vint8mf2x6_t", "vint8mf2x7_t", "vint8mf2x8_t",
+ "vint16mf2x2_t", "vint16mf2x3_t", "vint16mf2x4_t", "vint16mf2x5_t", "vint16mf2x6_t", "vint16mf2x7_t", "vint16mf2x8_t",
+ "vint32mf2x2_t", "vint32mf2x3_t", "vint32mf2x4_t", "vint32mf2x5_t", "vint32mf2x6_t", "vint32mf2x7_t", "vint32mf2x8_t",
+
+ # LMUL = m1
+ "vint8m1x2_t", "vint8m1x3_t", "vint8m1x4_t", "vint8m1x5_t", "vint8m1x6_t", "vint8m1x7_t", "vint8m1x8_t",
+ "vint16m1x2_t", "vint16m1x3_t", "vint16m1x4_t", "vint16m1x5_t", "vint16m1x6_t", "vint16m1x7_t", "vint16m1x8_t",
+ "vint32m1x2_t", "vint32m1x3_t", "vint32m1x4_t", "vint32m1x5_t", "vint32m1x6_t", "vint32m1x7_t", "vint32m1x8_t",
+ "vint64m1x2_t", "vint64m1x3_t", "vint64m1x4_t", "vint64m1x5_t", "vint64m1x6_t", "vint64m1x7_t", "vint64m1x8_t",
+
+ # LMUL = m2
+ "vint8m2x2_t", "vint8m2x3_t", "vint8m2x4_t",
+ "vint16m2x2_t", "vint16m2x3_t", "vint16m2x4_t",
+ "vint32m2x2_t", "vint32m2x3_t", "vint32m2x4_t",
+ "vint64m2x2_t", "vint64m2x3_t", "vint64m2x4_t",
+
+ # LMUL = m4
+ "vint8m4x2_t",
+ "vint16m4x2_t",
+ "vint32m4x2_t",
+ "vint64m4x2_t",
+ ]
+
+ vuint_tuple_types = [_.replace("int", "uint") for _ in vint_tuple_types]
+
+ vfloat_tuple_types = [
+ # vfloat16
+ "vfloat16mf4x2_t", "vfloat16mf4x3_t", "vfloat16mf4x4_t", "vfloat16mf4x5_t",
+ "vfloat16mf4x6_t", "vfloat16mf4x7_t", "vfloat16mf4x8_t",
+ "vfloat16mf2x2_t", "vfloat16mf2x3_t", "vfloat16mf2x4_t", "vfloat16mf2x5_t",
+ "vfloat16mf2x6_t", "vfloat16mf2x7_t", "vfloat16mf2x8_t",
+ "vfloat16m1x2_t", "vfloat16m1x3_t", "vfloat16m1x4_t", "vfloat16m1x5_t",
+ "vfloat16m1x6_t", "vfloat16m1x7_t", "vfloat16m1x8_t",
+ "vfloat16m2x2_t", "vfloat16m2x3_t", "vfloat16m2x4_t",
+ "vfloat16m4x2_t",
+
+ # LMUL = mf2 (1/2)
+ "vfloat32mf2x2_t", "vfloat32mf2x3_t", "vfloat32mf2x4_t", "vfloat32mf2x5_t",
+ "vfloat32mf2x6_t", "vfloat32mf2x7_t", "vfloat32mf2x8_t",
+
+ # LMUL = m1 (1)
+ "vfloat32m1x2_t", "vfloat32m1x3_t", "vfloat32m1x4_t", "vfloat32m1x5_t",
+ "vfloat32m1x6_t", "vfloat32m1x7_t", "vfloat32m1x8_t",
+ "vfloat64m1x2_t", "vfloat64m1x3_t", "vfloat64m1x4_t", "vfloat64m1x5_t",
+ "vfloat64m1x6_t", "vfloat64m1x7_t", "vfloat64m1x8_t",
+
+ # LMUL = m2 (2)
+ "vfloat32m2x2_t", "vfloat32m2x3_t", "vfloat32m2x4_t",
+ "vfloat64m2x2_t", "vfloat64m2x3_t", "vfloat64m2x4_t",
+
+ # LMUL = m4 (4)
+ "vfloat32m4x2_t",
+ "vfloat64m4x2_t",
+ ]
+ # fmt: on
+
+ def get_tuple_template(nfields: int) -> str:
+ tuple_template = tpl.module.tuple_func_template_start()
+ for i in range(nfields):
+ tuple_template += tpl.module.tuple_func_template_entry(i)
+ tuple_template += tpl.module.tuple_func_template_end()
+ return tuple_template
+
+ def get_main_tuple_template(nfields: int) -> str:
+ main_tuple_template = tpl.module.tuple_main_entry_template_start()
+ for i in range(nfields):
+ main_tuple_template += tpl.module.tuple_main_entry_template_entry(i)
+ main_tuple_template += tpl.module.tuple_main_entry_template_end()
+ return main_tuple_template
+
+ def get_test_tuple_template(nfields: int) -> str:
+ test_tuple_template = tpl.module.tuple_test_template_start(main_file)
+ for i in range(nfields):
+ test_tuple_template += tpl.module.tuple_test_template_entry_first(i)
+ test_tuple_template += tpl.module.tuple_test_template_entry_middle()
+ for i in range(nfields):
+ test_tuple_template += tpl.module.tuple_test_template_entry_second(i)
+ test_tuple_template += tpl.module.tuple_test_template_end(nfields)
+ return test_tuple_template
+
+ for type_name in vint_tuple_types + vuint_tuple_types + vfloat_tuple_types:
+ m = re.match(
+ r"v(int|uint|float)(8|16|32|64)(m|mf)(1|2|4|8)(x)([2-8])_t",
+ type_name,
+ )
+ if not m:
+ raise RuntimeError("wrong type")
+
+ elem_type = ElemType(m.group(1))
+ nfields = int(m.group(6))
+ short = type_name[:-4] + "_t"
+ big_suffix = "".join(m.group(2, 3, 4, 5, 6)) # 16m2x3
+ small_suffix = "".join(m.group(2, 3, 4)) # 16m2
+
+ func_name = tpl.module.func_name_template(type_name)
+ vsetvlmax = instr_templates.get(elem_type, InstrType.VSETVLMAX).format(
+ suffix1=small_suffix
+ )
+ vget_name = instr_templates.get(elem_type, InstrType.VGET).format(
+ suffix1=big_suffix, suffix2=small_suffix
+ )
+ vadd_name = instr_templates.get(elem_type, InstrType.VADD).format(
+ suffix1=small_suffix
+ )
+ vset_name = instr_templates.get(elem_type, InstrType.VSET).format(
+ suffix1=small_suffix, suffix2=big_suffix
+ )
+ vmv_name = instr_templates.get(elem_type, InstrType.VMV).format(
+ suffix1=small_suffix
+ )
+
+ var_idx = next(counter_vars)
+ var_values = [val for _, val in zip(range(nfields), counter_values)]
+ res_values = [2 * val for val in var_values]
+
+ string = get_tuple_template(nfields).format(
+ type_name=type_name,
+ short_type_name=short,
+ vget_name=vget_name,
+ vadd_name=vadd_name,
+ vset_name=vset_name,
+ func_name=func_name,
+ vsetvlmax=vsetvlmax,
+ )
+
+ with open(main_file_path, "a") as f:
+ f.write(string)
+
+ main_tail += get_main_tuple_template(nfields).format(
+ vsetvlmax=vsetvlmax,
+ short_type_name=short,
+ var_idx=var_idx,
+ small_suffix=small_suffix,
+ type_name=type_name,
+ vset_name=vset_name,
+ func_name=func_name,
+ vmv_name=vmv_name,
+ var_values=var_values,
+ )
+
+ break_idx = next(counter_break_idx)
+ test_command = get_test_tuple_template(nfields).format(
+ type_name=type_name,
+ break_idx=break_idx,
+ var_idx=var_idx,
+ var_values=var_values,
+ res_values=res_values,
+ func_name=func_name,
+ )
+
+ with open(test_script_path, "a") as f:
+ f.write(test_command)
+
+ with open(main_file_path, "a") as f:
+ f.write(main_tail)
+ f.write("\n return;\n}\n")
+ f.write("\nint main () {test();}\n")
+
+
+generate(WORK_DIR, TEST_NAME)
diff --git a/gdb/testsuite/gdb.arch/riscv-vector-abi-full.c b/gdb/testsuite/gdb.arch/riscv-vector-abi-full.c
new file mode 100644
index 00000000000..b8ab1fc1784
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vector-abi-full.c
@@ -0,0 +1,23 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+int
+main ()
+{
+ __asm__ __volatile__ ("vsetvli t0, x0, e8");
+ return 0; /* break 2 */
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vector-abi-full.exp b/gdb/testsuite/gdb.arch/riscv-vector-abi-full.exp
new file mode 100644
index 00000000000..3ee6c7edc15
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vector-abi-full.exp
@@ -0,0 +1,65 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+load_lib riscv64-rvv-lib.exp
+
+if {[catch {exec python3 -c "import jinja2"} result]} {
+ unsupported "python3 with jinja2 is required"
+ return
+}
+
+if {![riscv_support_rvv]} {
+ unsupported "RVV unsupported"
+ return
+}
+
+if {![riscv_support_rvv_intrinsic]} {
+ unsupported "RVV intrinsic unsupported"
+ return
+}
+
+standard_testfile
+
+set compile_flags {"debug"}
+lappend compile_flags "additional_flags=-march=rv64gcv"
+
+# First, we figure out VLENB value to set correct vector extension to march
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+
+gdb_breakpoint "$srcfile:[gdb_get_line_number "break 2"]"
+gdb_continue_to_breakpoint "preparing stage"
+set vlenb [riscvlib_rvv_get_csr vlenb "$testfile"]
+
+set compile_flags {"debug"}
+if {$vlenb >= 16} {
+ lappend compile_flags "additional_flags=-march=rv64gcv_zvfh"
+} elseif {$vlenb >= 8} {
+ lappend compile_flags "additional_flags=-march=rv64gc_zve64d_zvfh"
+} else {
+ unsupported "Unsupported VLENB value: $vlenb"
+ return
+}
+
+set env(WORK_DIR) [standard_output_file ""]
+set env(TEST_NAME) riscv-vector-abi-full-generated
+exec python3 $srcdir/$subdir/riscv-vector-abi-full-generate.py
+
+source [standard_output_file riscv-vector-abi-full-generated.exp]
diff --git a/gdb/testsuite/gdb.arch/riscv-vector-abi.c b/gdb/testsuite/gdb.arch/riscv-vector-abi.c
new file mode 100644
index 00000000000..120e2e2fd1e
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vector-abi.c
@@ -0,0 +1,157 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <riscv_vector.h>
+#include <malloc.h>
+
+unsigned
+do_vlen_read ()
+{
+ unsigned vlenb;
+ asm volatile ("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb) : :);
+ /* According to vector spec: "vlenb holds the value VLEN/8". */
+ return vlenb * 8;
+}
+
+vint64m1_t
+foo (vint64m1_t a, vint32mf2_t b, vint64m1_t c, size_t n)
+{
+ vint64m1_t tmp = __riscv_vwadd_wv_i64m1 (a, b, n);
+ return __riscv_vadd_vv_i64m1 (c, tmp, n);
+}
+
+vint32m4_t
+foo1 (vint32m4_t a, vint16m2_t b, size_t n)
+{
+ return __riscv_vwadd_wv_i32m4 (a, b, n);
+}
+
+vint32m4_t
+foo2 (vint16m2_t a, vint32m4_t b, size_t n)
+{
+ return __riscv_vwadd_wv_i32m4 (b, a, n);
+}
+
+vint64m8_t
+foo3 (vint64m8_t a, vint64m8_t b, vint64m8_t c, size_t n)
+{
+ vint64m8_t tmp = __riscv_vadd_vv_i64m8 (a, b, n);
+ return __riscv_vadd_vv_i64m8 (tmp, c, n);
+}
+
+vint64m8_t
+foo4 (vint64m8_t a, vint64m8_t b, vbool8_t mask, vbool8_t mask2, size_t n)
+{
+ return __riscv_vadd_vv_i64m8_m (mask2, a, b, n);
+}
+
+vint32m4_t
+foo5_get0 (vint16m2_t tmp_a, vint32m4x2_t a)
+{
+ return __riscv_vget_v_i32m4x2_i32m4 (a, 0);
+}
+
+vint32m4_t
+foo5_get1 (vint16m2_t tmp_a, vint32m4x2_t a)
+{
+ return __riscv_vget_v_i32m4x2_i32m4 (a, 1);
+}
+
+vint32mf2_t
+foo6_get0 (vint16m2_t tmp_a, vint32mf2x2_t a)
+{
+ return __riscv_vget_v_i32mf2x2_i32mf2 (a, 0);
+}
+
+vint32mf2_t
+foo6_get1 (vint16m2_t tmp_a, vint32mf2x2_t a)
+{
+ return __riscv_vget_v_i32mf2x2_i32mf2 (a, 1);
+}
+
+int
+main ()
+{
+ unsigned n = do_vlen_read () / 64;
+ vint64m1_t a = __riscv_vmv_v_x_i64m1 (42, n);
+ vint32mf2_t b = __riscv_vmv_v_x_i32mf2 (43, n);
+ vint64m1_t c = __riscv_vmv_v_x_i64m1 (44, n);
+
+ vint64m1_t res = foo (a, b, c, n);
+ /* break 2 */
+
+ n = do_vlen_read () * 4 / 32;
+ vint32m4_t g = __riscv_vmv_v_x_i32m4 (48, n);
+ vint16m2_t h = __riscv_vmv_v_x_i16m2 (49, n);
+
+ vint32m4_t res_1 = foo1 (g, h, n); // g is on v8-v11, h is on v12-v13
+ vint32m4_t res_2 = foo2 (h, g, n); // h is on v8-v9, g is on v12-v15
+ /* break 3 */
+
+ n = do_vlen_read () * 8 / 64;
+ vint64m8_t big1 = __riscv_vmv_v_x_i64m8 (50, n);
+ vint64m8_t big2 = __riscv_vmv_v_x_i64m8 (51, n);
+ vint64m8_t big3 = __riscv_vmv_v_x_i64m8 (52, n);
+ vint64m8_t big_res = foo3 (big1, big2, big3, n);
+ /* break 4 */
+
+ unsigned mask_size = n / 8;
+ uint8_t *rs1_mask = malloc (mask_size * sizeof (uint8_t));
+ for (int i = 0; i < mask_size; i++)
+ rs1_mask[i] = 0xa5;
+
+ vbool8_t mask = __riscv_vlm_v_b8 (rs1_mask, n);
+ vint64m8_t masked_sum = foo4 (big1, big2, mask, mask, n);
+ /* break 5 */
+
+ n = do_vlen_read () * 4 * 2 / 32;
+ unsigned addr_size = n / 2;
+ uint32_t *addr = malloc (addr_size * sizeof (uint32_t));
+ for (unsigned i = 0; i < addr_size; i++)
+ addr[i] = 8 * (uint32_t)i;
+
+ vuint32m4_t rs2 = __riscv_vle32_v_u32m4 (addr, addr_size);
+ /* break 6 */
+
+ int32_t *rs1 = malloc (n);
+ for (int i = 0; i < n; i++)
+ rs1[i] = (int32_t)i;
+
+ vint32m4x2_t a_seg = __riscv_vluxseg2ei32_v_i32m4x2 (rs1, rs2, n);
+ /* break 7 */
+ vint32m4_t res_a_seg0 = foo5_get0 (h, a_seg);
+ /* break 8 */
+ vint32m4_t res_a_seg1 = foo5_get1 (h, a_seg);
+ /* break 9 */
+
+ vuint32mf2_t rs2_2 = __riscv_vle32_v_u32mf2 (addr, addr_size);
+ /* break 10 */
+
+ n = do_vlen_read () / 32;
+ vint32mf2x2_t b_seg = __riscv_vluxseg2ei32_v_i32mf2x2 (rs1, rs2_2, n);
+ /* break 11 */
+ vint32mf2_t res_b_seg0 = foo6_get0 (h, b_seg);
+ /* break 12 */
+ vint32mf2_t res_b_seg1 = foo6_get1 (h, b_seg);
+ /* break 13 */
+
+ free (rs1_mask);
+ free (addr);
+ free (rs1);
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vector-abi.exp b/gdb/testsuite/gdb.arch/riscv-vector-abi.exp
new file mode 100644
index 00000000000..00b046f2f1f
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vector-abi.exp
@@ -0,0 +1,230 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+load_lib riscv64-rvv-lib.exp
+
+set get_vl_called_times 0
+
+proc get_vl {} {
+ global hex
+ global decimal
+ global gdb_prompt
+ global gdb_test_name
+ global get_vl_called_times
+
+ set vl 0x0
+
+ gdb_test_multiple "info registers \$vl" "$get_vl_called_times call of get_vl()" {
+ -re "^info registers\[^\r\n\]+\r\n" {
+ exp_continue
+ }
+
+ -re "^vl\\s+(${hex})\\s+(${decimal})\r\n" {
+ set vl $expect_out(2,string)
+ exp_continue
+ }
+
+ -re "^$gdb_prompt $" {
+ pass $gdb_test_name
+ }
+ }
+
+ set get_vl_called_times [expr {$get_vl_called_times + 1}]
+
+ return $vl
+}
+
+proc generate_sequence { start step count } {
+ if {$step == 0 && $count > 8} {
+ return "$start <repeats $count times>"
+ }
+
+ set res "$start"
+ set count [expr {$count - 1}]
+
+ for {set i 0} {$i < $count} {incr i} {
+ set start [expr {$start + $step}]
+ set res "${res}, $start"
+ }
+
+ return $res
+}
+
+if {![riscv_support_rvv]} {
+ unsupported "RVV unsupported"
+ return
+}
+
+if {![riscv_support_rvv_intrinsic]} {
+ unsupported "RVV intrinsic unsupported"
+ return
+}
+
+standard_testfile
+
+set compile_flags {"debug"}
+lappend compile_flags "additional_flags=-march=rv64gcv"
+
+# First, we figure out VLENB value to set correct vector extension to march
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+
+gdb_breakpoint "$srcfile:[gdb_get_line_number "break 2"]"
+gdb_continue_to_breakpoint "preparing stage"
+set vlenb [riscvlib_rvv_get_csr vlenb "$testfile"]
+
+set compile_flags {"debug"}
+if {$vlenb >= 16} {
+ lappend compile_flags "additional_flags=-march=rv64gcv"
+} elseif {$vlenb >= 8} {
+ lappend compile_flags "additional_flags=-march=rv64gc_zve64x"
+} else {
+ unsupported "Unsupported VLENB value: $vlenb"
+ return
+}
+
+# Here is real test started
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+
+for {set i 2} {$i <= 13} {incr i} {
+ gdb_breakpoint "$srcfile:[gdb_get_line_number "break $i"]"
+}
+
+gdb_continue_to_breakpoint "break 2"
+
+set vl [get_vl]
+
+gdb_test "print a" "\\{[generate_sequence 42 0 $vl]\\}"
+gdb_test "print b" "\\{[generate_sequence 43 0 $vl]\\}"
+gdb_test "print c" "\\{[generate_sequence 44 0 $vl]\\}"
+gdb_test "print res" "\\{[generate_sequence 129 0 $vl]\\}"
+gdb_test "print foo(a, b, c, n)" "\\{[generate_sequence 129 0 $vl]\\}"
+
+gdb_continue_to_breakpoint "break 3"
+
+set vl [get_vl]
+
+gdb_test "print g" "\\{[generate_sequence 48 0 $vl]\\}"
+gdb_test "print h" "\\{[generate_sequence 49 0 $vl]\\}"
+
+gdb_test "print res_1" "\\{[generate_sequence 97 0 $vl]\\}"
+gdb_test "print foo1(g, h, n)" "\\{[generate_sequence 97 0 $vl]\\}"
+
+gdb_test "print res_2" "\\{[generate_sequence 97 0 $vl]\\}"
+gdb_test "print foo2(h, g, n)" "\\{[generate_sequence 97 0 $vl]\\}"
+
+gdb_continue_to_breakpoint "break 4"
+
+set vl [get_vl]
+
+gdb_test "print big1" "\\{[generate_sequence 50 0 $vl]\\}"
+gdb_test "print big2" "\\{[generate_sequence 51 0 $vl]\\}"
+gdb_test "print big3" "\\{[generate_sequence 52 0 $vl]\\}"
+gdb_test "print big_res" "\\{[generate_sequence 153 0 $vl]\\}"
+gdb_test "print foo3(big1, big2, big3, n)" "\\{[generate_sequence 153 0 $vl]\\}"
+
+gdb_continue_to_breakpoint "break 5"
+
+set vl [get_vl]
+set repeat_num [expr {$vl / 8 - 1}]
+
+set pattern_part "101, 51, 101, 51, 51, 101, 51, 101"
+set pattern "\\{$pattern_part"
+for {set i 0} {$i < $repeat_num} {incr i} {
+ set pattern "$pattern, $pattern_part"
+}
+set pattern "$pattern\\}"
+
+gdb_test "print masked_sum" $pattern
+gdb_test "print foo4(big1, big2, mask, mask, n)" $pattern
+
+gdb_continue_to_breakpoint "break 6"
+
+set vl [get_vl]
+
+set pattern "\\{[generate_sequence 0 8 $vl]\\}"
+
+gdb_test "print rs2" $pattern
+
+gdb_continue_to_breakpoint "break 7"
+
+set vl [get_vl]
+
+set pattern "= \\{\\{[generate_sequence 0 2 $vl].*\\}, \\{[generate_sequence 1 2 $vl].*\\}\\}"
+
+gdb_test "print a_seg" $pattern
+
+gdb_continue_to_breakpoint "break 8"
+
+set vl [get_vl]
+
+set pattern "= \\{[generate_sequence 0 2 $vl].*\\}"
+
+gdb_test "print res_a_seg0" $pattern
+gdb_test "print foo5_get0(h, a_seg)" $pattern
+
+gdb_continue_to_breakpoint "break 9"
+
+set vl [get_vl]
+
+set pattern "= \\{[generate_sequence 1 2 $vl].*\\}"
+
+gdb_test "print res_a_seg1" $pattern
+gdb_test "print foo5_get1(h, a_seg)" $pattern
+
+gdb_continue_to_breakpoint "break 10"
+
+set vl [get_vl]
+
+set pattern "\\{[generate_sequence 0 8 $vl]\\}"
+
+gdb_test "print rs2_2" $pattern
+
+gdb_continue_to_breakpoint "break 11"
+
+set vl [get_vl]
+
+set pattern "= \\{\\{[generate_sequence 0 2 $vl].*\\}, \\{[generate_sequence 1 2 $vl].*\\}\\}"
+
+gdb_test "print b_seg" $pattern
+
+gdb_continue_to_breakpoint "break 12"
+
+set vl [get_vl]
+
+set pattern "= \\{[generate_sequence 0 2 $vl].*\\}"
+
+gdb_test "print res_b_seg0" $pattern
+gdb_test "print foo6_get0(h, b_seg)" $pattern
+
+gdb_continue_to_breakpoint "break 13"
+
+set vl [get_vl]
+
+set pattern "= \\{[generate_sequence 1 2 $vl].*\\}"
+
+gdb_test "print res_b_seg1" $pattern
+gdb_test "print foo6_get1(h, b_seg)" $pattern
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-availability.c b/gdb/testsuite/gdb.arch/riscv-vu-availability.c
new file mode 100644
index 00000000000..620ecb7142d
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-availability.c
@@ -0,0 +1,67 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+asm (".option arch, +v\n");
+
+unsigned
+do_vlenb_read ()
+{
+ unsigned vlenb;
+ asm volatile ("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb) : :);
+ return vlenb;
+}
+
+unsigned
+do_vsetvli ()
+{
+ unsigned vl;
+ asm volatile ("vsetvli %[new_vl], x0, e8, m1, ta, ma"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ return vl;
+}
+
+#ifdef READ_VLENB_BEFORE_MAIN
+unsigned VLENB = do_vlenb_read ();
+#endif // READ_VLENB_BEFORE_MAIN
+
+#ifdef SET_VSETVLI_BEFORE_MAIN
+unsigned VL = do_vsetvli ();
+#endif // SET_VSETVLI_BEFORE_MAIN
+
+int STORAGE[64];
+
+void
+do_vector_stuff ()
+{
+ do_vsetvli ();
+ asm volatile ("vadd.vi v1, v1, 0x1");
+ asm volatile ("vadd.vi v2, v1, 0x2");
+ asm volatile ("vs1r.v v1, (%0)"
+ :
+ : "r"(STORAGE)
+ : "memory"); /* pre_vect_mem */
+ asm volatile ("vl1re8.v v2, (%0)" : : "r"(STORAGE) : "memory");
+}
+
+int
+main ()
+{
+ do_vector_stuff ();
+ return 0; /* post_vector_op */
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-availability.exp b/gdb/testsuite/gdb.arch/riscv-vu-availability.exp
new file mode 100644
index 00000000000..dbbb010f055
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-availability.exp
@@ -0,0 +1,72 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+require {istarget "riscv*-*-*"}
+
+if {![riscv_support_rvv]} {
+ unsupported "RVV unsupported"
+ return
+}
+
+standard_testfile
+load_lib riscv64-rvv-lib.exp
+
+proc initialize_vu_availability_test {extra_args } {
+ global testfile
+ global srcfile
+
+ set compile_flags {}
+ lappend compile_flags debug
+ lappend compile_flags c++
+ lappend compile_flags "additional_flags=-march=rv64gc ${extra_args}"
+
+ if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+ }
+
+ if {![runto_main]} {
+ return -1
+ }
+}
+
+proc test_unavailable_regs { extra_args } {
+ initialize_vu_availability_test ${extra_args}
+ gdb_test "print \$vtype" "= <unavailable>" "test vtype unavailable ${extra_args}"
+ gdb_test "print \$vcsr" "= <unavailable>" "test vcsr unavailable ${extra_args}"
+ gdb_test "print \$vl" "= <unavailable>" "test vl unavailable ${extra_args}"
+ gdb_test "print \$vstart" "= <unavailable>" "test vstart unavailable ${extra_args}"
+ gdb_test "print \$vlenb" "= <unavailable>" "test vlenb unavailable ${extra_args}"
+ for {set i 0} {$i < 32} {incr i} {
+ gdb_test "print \$v${i}" "= <unavailable>" "test v${i} unavailable ${extra_args}"
+ }
+}
+
+proc test_available_regs { extra_args } {
+ global testfile
+ initialize_vu_availability_test ${extra_args}
+ set VLENB [riscvlib_rvv_get_csr vlenb "$testfile"]
+ gdb_test "print \$vtype" "= 192" "test vtype available ${extra_args}"
+ gdb_test "print \$vcsr" "= 0" "test vcsr available ${extra_args}"
+ gdb_test "print \$vl" "= $VLENB" "test vl available ${extra_args}"
+ gdb_test "print \$vstart" "= 0" "test vstart available ${extra_args}"
+ gdb_test "print \$vlenb" "= $VLENB" "test vlenb available ${extra_args}"
+ for {set i 0} {$i < 32} {incr i} {
+ gdb_test "print \$v${i}" [riscvlib_rvv_vreg_zero_pattern $VLENB] "test v${i} available ${extra_args}"
+ }
+}
+
+test_unavailable_regs "-DREAD_VLENB_BEFORE_MAIN"
+test_unavailable_regs ""
+test_available_regs "-DSET_VSETVLI_BEFORE_MAIN"
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-consitency-checks.c b/gdb/testsuite/gdb.arch/riscv-vu-consitency-checks.c
new file mode 100644
index 00000000000..f392b0a2df3
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-consitency-checks.c
@@ -0,0 +1,79 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+asm (".option arch, +v\n");
+
+#include <stdlib.h>
+#include <limits.h>
+#include <stdint.h>
+
+unsigned
+do_vlenb_read ()
+{
+ unsigned vlenb;
+ asm volatile ("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb) : :);
+ return vlenb;
+}
+
+void
+reset_vu ()
+{
+ unsigned vl;
+ asm volatile ("vsetvli %[new_vl], x0, e8, m8, ta, ma"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ asm volatile ("vxor.vv v0, v0, v0\n"
+ "vxor.vv v8, v8, v8\n"
+ "vxor.vv v16, v16, v16\n"
+ "vxor.vv v24, v24, v24\n"
+ "csrrci zero, vxrm, 3\n"
+ "csrrci zero, vxsat, 1\n");
+ asm volatile ("vsetvli %[new_vl], x0, e8, m1, tu, mu"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ asm volatile ("nop"); /* vu_reset_end */
+}
+
+void
+do_workload ()
+{
+ unsigned long long app_vtype;
+ unsigned app_vl;
+ unsigned app_vlenb;
+ asm volatile ("csrr %[vtype], vtype\n" : [vtype] "=r"(app_vtype) : :);
+ asm volatile ("csrr %[vl], vl\n"
+ : [vl] "=r"(app_vl) /* vect_test_vtype_read */
+ :
+ :);
+ asm volatile ("csrr %[vlenb], vlenb\n" : [vlenb] "=r"(app_vlenb) : :);
+ asm volatile ("vxor.vv v24, v16, v8\n" : : :);
+ asm volatile ("nop"); /* workload_end */
+}
+
+int
+main ()
+{
+ unsigned vlenb_value = do_vlenb_read ();
+ (void)vlenb_value;
+ reset_vu ();
+ /* vect_test_start */
+ for (int i = 0; i < 777; ++i)
+ do_workload ();
+ return 0; /* vect_test_end */
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-consitency-checks.exp b/gdb/testsuite/gdb.arch/riscv-vu-consitency-checks.exp
new file mode 100644
index 00000000000..9f52d1dcbd4
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-consitency-checks.exp
@@ -0,0 +1,152 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+require {istarget "riscv*-*-*"}
+
+if {![riscv_support_rvv]} {
+ unsupported "RVV unsupported"
+ return
+}
+
+standard_testfile
+load_lib riscv64-rvv-lib.exp
+
+proc test_vu_consistency_vl_overflow {VLENB} {
+ set the_proc [lindex [info level 0] 0]
+
+ gdb_continue_to_breakpoint "$the_proc: start"
+ gdb_test "print app_vtype" "= 0" "$the_proc: app vtype"
+ gdb_test "print app_vl" "= $VLENB" "$the_proc: app vl"
+ gdb_test "print app_vlenb" "= $VLENB" "$the_proc: app vlenb"
+ gdb_test_no_output "set \$vl = 9999"
+
+ gdb_continue_to_breakpoint "$the_proc: vl updated"
+ gdb_test "print app_vtype" "= 0" "$the_proc: app vtype - after vl update"
+ gdb_test "print app_vl" "= $VLENB" "$the_proc: app vl - after vl update"
+ gdb_test "print app_vlenb" "= $VLENB" "$the_proc: app vlenb - after vl update"
+ gdb_test "print \$vl" "= $VLENB" "$the_proc: ptraced vl - after vl update"
+}
+
+proc test_vu_coherent_vl_lmul_downgrade {VLENB} {
+ set the_proc [lindex [info level 0] 0]
+
+ gdb_continue_to_breakpoint "$the_proc: start"
+ gdb_test_no_output "set \$vtype = 3"
+
+ gdb_continue_to_breakpoint "$the_proc: run with updated LMUL 8"
+ gdb_test_no_output "set \$vl = 9999" "set \$vl = 9999 large value"
+
+ gdb_continue_to_breakpoint "$the_proc: run with updated large vl"
+ gdb_test "print app_vtype" "= 3" "$the_proc: app vtype - after vtype LMUL 8 update"
+ gdb_test "print app_vl" "= [expr {$VLENB * 8}]" "$the_proc: app vl - after vl LMUL 8 update"
+ gdb_test "print app_vlenb" "= $VLENB" "$the_proc: app vlenb - after LMUL 8 update"
+ gdb_test "print \$vl" "= [expr {$VLENB * 8}]" "$the_proc: ptraced vl - VLENB * 8"
+ gdb_test "print \$vtype" "= 3" "$the_proc: ptraced vtype - 3"
+
+ gdb_continue_to_breakpoint "$the_proc: going to switch LMUL back to 1"
+ gdb_test_no_output "set \$vtype = 0"
+
+ gdb_continue_to_breakpoint "$the_proc: LMUL should be 1"
+ gdb_test "print app_vtype" "= 0" "$the_proc: app vtype - after LMUL 1 update"
+ gdb_test "print app_vl" "= $VLENB" "$the_proc: app vl - after LMUL 1 update"
+ gdb_test "print app_vlenb" "= $VLENB" "$the_proc: app vlenb - after LMUL 1 update"
+ gdb_test "print \$vl" "= $VLENB" "$the_proc: ptraced vl - after LMUL 1 update"
+ gdb_test "print \$vtype" "= 0" "$the_proc: ptraced vtype - after LMUL 1 update"
+}
+
+proc test_vu_coherent_non_zero_vstart {VLENB} {
+ set the_proc [lindex [info level 0] 0]
+ gdb_continue_to_breakpoint "$the_proc: messing up vstart"
+ gdb_test_no_output "set \$vstart = 8"
+
+ gdb_continue_to_breakpoint "$the_proc: vstart was 8"
+ gdb_test "print \$vstart" "= 0" "$the_proc: ptraced vstart - after vstart update"
+}
+
+proc test_vu_consistency_incorrect_vtype {VLENB} {
+ global srcfile
+ set the_proc [lindex [info level 0] 0]
+ if {$VLENB >= 64} {
+ untested "$the_proc: VLENB must be less than 64"
+ return
+ }
+ gdb_continue_to_breakpoint "$the_proc: setting SEW to 64 and LMUL to 1/8"
+ set incorrect_vtype [expr { 5 | (3 << 3)}]
+ gdb_test_no_output "set \$vtype = $incorrect_vtype"
+ gdb_test "stepi" ".*" "$the_proc: stepi after incorrect"
+ # kernel BUG: this should not match
+ gdb_test "print/ \$vtype" "= 0x8000000000000000" "$the_proc: ptraced vtype - after setting illegal mode"
+
+ gdb_breakpoint $srcfile:[gdb_get_line_number vect_test_vtype_read] temporary
+ gdb_continue_to_breakpoint "$the_proc: app vtype read"
+ gdb_test "print/x app_vtype" "= 0x8000000000000000" "$the_proc: app vtype- after setting illegal mode"
+ gdb_test "print/ \$vtype" "= 0x8000000000000000" "$the_proc: ptraced vtype - still illegal"
+ gdb_test_no_output "set \$vtype = 0" "$the_proc: legalizing vtype"
+
+ gdb_continue_to_breakpoint "$the_proc: vtype is legal again"
+ gdb_test "print app_vl" "= 0" "$the_proc: app vl - after vtype legalized"
+ gdb_test "print app_vlenb" "= $VLENB" "$the_proc: app vlenb - after vtype legalized"
+ # kernel BUG: this should match app_vl
+ gdb_test "print \$vl" "= 0" "$the_proc: ptraced vl - after vtype legalizied"
+ gdb_test "print \$vtype" "= 0" "$the_proc: ptraced vtype - after vtype legalized"
+ gdb_test_no_output "set \$vl = $VLENB" "$the_proc: setting vl after vtype legalized"
+
+ gdb_continue_to_breakpoint "$the_proc: everything is legalized"
+ gdb_test "print app_vl" "= $VLENB" "$the_proc: app vl - really legal app vl"
+ gdb_test "print app_vlenb" "= $VLENB" "$the_proc: app vlenb - really legal app vtype"
+ gdb_test "print \$vl" "= $VLENB" "$the_proc: ptraced vl - really legal"
+ gdb_test "print \$vtype" "= 0" "$the_proc: ptraced vtype - really legal"
+}
+
+proc test_vu_do_consistency_test {VLENB} {
+ global srcfile
+ gdb_breakpoint "$srcfile:[gdb_get_line_number workload_end]"
+
+ gdb_continue_to_breakpoint "warm up"
+
+ test_vu_consistency_vl_overflow $VLENB
+ test_vu_coherent_vl_lmul_downgrade $VLENB
+ test_vu_coherent_non_zero_vstart $VLENB
+ test_vu_consistency_incorrect_vtype $VLENB
+}
+
+proc prepare_vu_consistency_test {} {
+ global testfile
+ global srcfile
+
+ set compile_flags {}
+ lappend compile_flags debug
+ lappend compile_flags "additional_flags=-march=rv64gc"
+
+ if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+ }
+
+ if {![runto_main]} {
+ return -1
+ }
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_test_start]"
+ gdb_continue_to_breakpoint "vect_test_start"
+
+ return 0
+}
+
+if {[prepare_vu_consistency_test]} {
+ untested "could not initialize"
+ return -1
+}
+set vlenb [riscvlib_rvv_get_csr vlenb "$testfile"]
+test_vu_do_consistency_test $vlenb
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-ctx-print.c b/gdb/testsuite/gdb.arch/riscv-vu-ctx-print.c
new file mode 100644
index 00000000000..b947c7834ea
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-ctx-print.c
@@ -0,0 +1,106 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <vector>
+
+asm (".option arch, +v\n");
+
+enum VLMUL
+{
+ LMUL1 = 0,
+ LMUL2 = 1,
+ LMUL4 = 2,
+ LMUL8 = 3,
+ LMUL_F8 = 5,
+ LMUL_F4 = 6,
+ LMUL_F2 = 7
+};
+
+enum SEW
+{
+ SEW8 = 0,
+ SEW16 = 1,
+ SEW32 = 2,
+ SEW64 = 3,
+};
+
+unsigned
+do_vsetvli ()
+{
+ unsigned vl;
+ asm volatile ("vsetvli %[new_vl], x0, e8, m1, ta, ma"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ return vl;
+}
+
+unsigned
+do_vsetv (unsigned vl, VLMUL vlmul, SEW vsew, unsigned vta, unsigned vma)
+{
+ unsigned vtype
+ = (unsigned)vlmul | ((unsigned)vsew << 3) | (vta << 6) | (vma << 7);
+ asm volatile ("vsetvl %[new_vl], %[new_vl], %[vtype]"
+ : [new_vl] "+r"(vl)
+ : [vtype] "r"(vtype)
+ :);
+ return vl; /* vsetvl_done */
+}
+
+int STORAGE[64];
+
+void
+do_vector_stuff ()
+{
+ std::vector<VLMUL> vlmul = {
+ VLMUL::LMUL1, VLMUL::LMUL2, VLMUL::LMUL4, VLMUL::LMUL8,
+ VLMUL::LMUL_F8, VLMUL::LMUL_F4, VLMUL::LMUL_F2,
+ };
+ std::vector<SEW> vsew = {
+ SEW::SEW8,
+ SEW::SEW16,
+ SEW::SEW32,
+ SEW::SEW64,
+ };
+ for (auto vlmul : vlmul)
+ for (auto sew : vsew)
+ for (int vta = 0; vta < 2; ++vta)
+ for (int vma = 0; vma < 2; ++vma)
+ for (int vl = 1; vl < 3; ++vl)
+ do_vsetv (vl, vlmul, sew, vta, vma);
+
+ asm volatile ("csrw vxrm, %[rnd_m]" : : [rnd_m] "i"(0) :);
+ asm volatile ("csrw vxrm, %[rnd_m]" : : [rnd_m] "i"(1) :); /* vxrm_0 */
+ asm volatile ("csrw vxrm, %[rnd_m]" : : [rnd_m] "i"(2) :); /* vxrm_1 */
+ asm volatile ("csrw vxrm, %[rnd_m]" : : [rnd_m] "i"(3) :); /* vxrm_2 */
+ asm volatile ("csrw vxsat, %[vxsat]" : : [vxsat] "i"(1) :); /* vxrm_3 */
+ asm volatile ("csrw vxrm, %[rnd_m]" : : [rnd_m] "i"(0) :); /* vxrm_0_again */
+ unsigned vtype = -1;
+ unsigned vl = -1;
+ asm volatile ("vsetvl %[new_vl], %[new_vl], %[vtype]"
+ : [new_vl] "+r"(vl), [vtype] "=r"(vtype)
+ :
+ :); /* vcsr_done */
+}
+
+int
+main ()
+{
+ do_vsetvli ();
+ do_vector_stuff (); /* rvv_initialized */
+ return 0; /* do_vector_stuff_done */
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-ctx-print.exp b/gdb/testsuite/gdb.arch/riscv-vu-ctx-print.exp
new file mode 100644
index 00000000000..9c4c6079be6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-ctx-print.exp
@@ -0,0 +1,107 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+require {istarget "riscv*-*-*"}
+
+if {![riscv_support_rvv]} {
+ unsupported "RVV unsupported"
+ return
+}
+
+standard_testfile
+load_lib riscv64-rvv-lib.exp
+
+proc test_vu_ctx_printouts {VLENB} {
+ global testfile
+ global srcfile
+ global hex
+
+ array set vlmul { 0 1 1 2 2 4 3 8 7 1/2 6 1/4 5 1/8 }
+ array set vsew { 0 e8 1 e16 2 e32 3 e64 }
+ array set vta { 0 tu 1 ta }
+ array set vma { 0 mu 1 ma }
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vsetvl_done]"
+
+ foreach lmul [lsort -integer [array names vlmul]] {
+ foreach sew [lsort -integer [array names vsew]] {
+ foreach ta [lsort -integer [array names vta]] {
+ foreach ma [lsort -integer [array names vma]] {
+ foreach vl {1 2} {
+ set slmul $vlmul($lmul)
+ set ssew $vsew($sew)
+ set sta $vta($ta)
+ set sma $vma($ma)
+ set case_id "vlmul: $lmul, sew: $sew, ta: $ta, ma: $ma, vl: $vl"
+ gdb_continue_to_breakpoint "vsetvl_done lmul / $case_id"
+
+ if {![riscvlib_is_vlmul_vsew_legal $VLENB $lmul $sew]} {
+ set vtype_pattern "vill:1"
+ } else {
+ set vtype_pattern "$hex\tLMUL:$lmul \\($slmul\\) SEW:$sew \\($ssew\\) vta:$ta \\($sta\\) vma:$ma \\($sma\\) vill:0"
+ }
+ gdb_test "info reg vtype" "${vtype_pattern}" "info reg vtype: $case_id"
+ set fvl [riscvlib_get_allowed_vl $VLENB $lmul $sew $vl]
+ gdb_test "info reg vl" "^vl\\s+[format 0x%x $fvl]\t$fvl" "info reg vl: $case_id, fvl: $fvl"
+ }
+ }
+ }
+ }
+ }
+
+ foreach vxrm {0 1 2 3} {
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vxrm_$vxrm]"
+ gdb_continue_to_breakpoint "vxrm_$vxrm"
+ set vcsr_value_hex [format 0x%x [expr { ($vxrm << 1) }]]
+ gdb_test "info reg vcsr" "^vcsr\\s+$vcsr_value_hex\tVXSAT:0 VXRM:$vxrm" "info reg vcsr: vxrm_$vxrm"
+ }
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vxrm_0_again]"
+ gdb_continue_to_breakpoint "vxrm_0_again"
+ gdb_test "info reg vcsr" "^vcsr\\s+0x7\tVXSAT:1 VXRM:3" "info reg vcsr: vxsat_1"
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vcsr_done]"
+ gdb_continue_to_breakpoint "vcsr_done"
+ gdb_test "info reg vcsr" "^vcsr\\s+0x1\tVXSAT:1 VXRM:0" "info reg vcsr: vxsat_1_vxrm0"
+}
+
+proc prepare_vu_printout_test {} {
+ global testfile
+ global srcfile
+
+ set compile_flags {}
+ lappend compile_flags debug
+ lappend compile_flags c++
+ lappend compile_flags "additional_flags=-march=rv64gc"
+
+ if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+ }
+
+ if {![runto_main]} {
+ return -1
+ }
+
+ return 0
+}
+
+if {[prepare_vu_printout_test]} {
+ untested "could not initialize"
+ return -1
+}
+gdb_breakpoint "$srcfile:[gdb_get_line_number rvv_initialized]"
+gdb_continue_to_breakpoint "rvv_initialized"
+set vlenb [riscvlib_rvv_get_csr vlenb "$testfile"]
+
+test_vu_ctx_printouts $vlenb
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-printout.c b/gdb/testsuite/gdb.arch/riscv-vu-printout.c
new file mode 100644
index 00000000000..c87f71d8ee2
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-printout.c
@@ -0,0 +1,69 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <limits.h>
+
+asm (".option arch, +v\n");
+
+unsigned
+do_vlenb_read ()
+{
+ unsigned vlenb;
+ asm volatile ("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb) : :);
+ return vlenb;
+}
+
+unsigned
+do_vsetvli ()
+{
+ unsigned vl;
+ asm volatile ("vsetvli %[new_vl], x0, e8, m8, tu, mu"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ return vl;
+}
+
+char *STORAGE;
+
+void
+do_vector_stuff ()
+{
+ unsigned vlenb_value = do_vlenb_read ();
+ STORAGE = (char *)calloc (1, vlenb_value * CHAR_BIT);
+ do_vsetvli ();
+ asm volatile ("vxor.vv v0, v0, v0");
+ asm volatile ("vxor.vv v8, v8, v8");
+ asm volatile ("vxor.vv v16, v16, v16");
+ asm volatile ("vxor.vv v24, v24, v24");
+ asm volatile ("vsetvli t0, x0, e8, m1, tu, mu" : : : "t0");
+ asm volatile ("vadd.vi v1, v1, 0x1");
+ asm volatile ("vadd.vi v2, v1, 0x2");
+ asm volatile ("vs1r.v v1, (%0)"
+ :
+ : "r"(STORAGE)
+ : "memory"); /* pre_vect_mem */
+ asm volatile ("vl1re8.v v2, (%0)" : : "r"(STORAGE) : "memory");
+}
+
+int
+main ()
+{
+ do_vector_stuff ();
+ return 0; /* post_vector_op */
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-printout.exp b/gdb/testsuite/gdb.arch/riscv-vu-printout.exp
new file mode 100644
index 00000000000..e2fd65f4d9d
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-printout.exp
@@ -0,0 +1,92 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+require {istarget "riscv*-*-*"}
+
+if {![riscv_support_rvv]} {
+ unsupported "RVV unsupported"
+ return
+}
+
+standard_testfile
+load_lib riscv64-rvv-lib.exp
+
+proc test_vu_printouts {VLENB} {
+ global srcfile
+
+ set vtype_pattern "0x0\tLMUL:0 \\(1\\) SEW:0 \\(e8\\) vta:0 \\(tu\\) vma:0 \\(mu\\) vill:0"
+ gdb_test "info reg vtype" "${vtype_pattern}" "printout info reg vtype"
+ gdb_test "info reg vcsr" "0x0\tVXSAT:0 VXRM:0" "printout info reg vcsr"
+ gdb_test "info reg vl" "[format 0x%x $VLENB]\t$VLENB" "printout info reg vl"
+ gdb_test "info reg vstart" "0x0\t0" "printout info reg vstart"
+ gdb_test "info reg vlenb" "[format 0x%x $VLENB]\t$VLENB" "printout info reg vlenb"
+
+ set zero_pattern [riscvlib_rvv_vreg_zero_pattern $VLENB]
+ gdb_test "print \$v0" ${zero_pattern} "printout print v0"
+ gdb_test "print \$v1" [riscvlib_rvv_vreg_1_pattern $VLENB] "printout print v1"
+ gdb_test "print \$v2" [riscvlib_rvv_vreg_3_pattern $VLENB] "printout print v2"
+ for {set i 3} {$i < 32} {incr i} {
+ gdb_test "print \$v${i}" ${zero_pattern} "printout print v${i}"
+ }
+
+ set vregs [capture_command_output "info registers vector" ""]
+ foreach {- regname} [regexp -all -inline -line {^(\w+)\s+} $vregs] {
+ incr vreg_arr($regname)
+ }
+ set expected_list { vtype vcsr vl vstart vlenb }
+ for {set i 0 } { $i < 32 } { incr i } {
+ lappend expected_list v$i
+ }
+ set s_expc_list [lsort $expected_list]
+ set s_vreg_list [lsort [array names vreg_arr]]
+ if {![string equal $s_expc_list $s_vreg_list]} {
+ fail "info registers vector (contents)"
+ }
+ foreach reg $expected_list {
+ if { $vreg_arr($reg) != 1 } {
+ fail "info registers vector has duplicated $reg"
+ } else {
+ pass "info register vector has $reg"
+ }
+ }
+}
+
+proc prepare_vu_printout_test {} {
+ global testfile
+ global srcfile
+
+ set compile_flags {}
+ lappend compile_flags debug
+ lappend compile_flags "additional_flags=-march=rv64gc"
+
+ if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+ }
+
+ if {![runto_main]} {
+ return -1
+ }
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number pre_vect_mem]"
+ gdb_continue_to_breakpoint "pre_vect_mem"
+ return 0
+}
+
+if {[prepare_vu_printout_test]} {
+ untested "could not initialize"
+ return -1
+}
+set vlenb [riscvlib_rvv_get_csr vlenb "$testfile"]
+test_vu_printouts $vlenb
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-rvv-unsupported.c b/gdb/testsuite/gdb.arch/riscv-vu-rvv-unsupported.c
new file mode 100644
index 00000000000..8a9284e5a5f
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-rvv-unsupported.c
@@ -0,0 +1,23 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+int
+main ()
+{
+ int a = 42;
+ return 0; /* break 2 */
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-rvv-unsupported.exp b/gdb/testsuite/gdb.arch/riscv-vu-rvv-unsupported.exp
new file mode 100644
index 00000000000..7205ec8d810
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-rvv-unsupported.exp
@@ -0,0 +1,46 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+require {istarget "riscv*-*-*"}
+
+if {[riscv_support_rvv]} {
+ unsupported "need to run on targets without RVV support"
+ return
+}
+
+standard_testfile
+
+set compile_flags {"debug"}
+lappend compile_flags "additional_flags=-march=rv64gcv"
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+
+gdb_breakpoint "$srcfile:[gdb_get_line_number "break 2"]"
+gdb_continue_to_breakpoint "break 2"
+
+gdb_test "print a" " = 42"
+
+set a0_val 42
+set a0_hex_val 0x[format %x $a0_val]
+gdb_test_no_output "set \$a0 = $a0_val"
+gdb_test "info reg a0" "a0\[ \t\]+$a0_hex_val\[ \t\]+$a0_val"
+
+gdb_test "info reg v0" "Invalid register `v0'"
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-rwr.c b/gdb/testsuite/gdb.arch/riscv-vu-rwr.c
new file mode 100644
index 00000000000..05874b1fdb3
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-rwr.c
@@ -0,0 +1,62 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+asm (".option arch, +v\n");
+
+#include <stdlib.h>
+#include <limits.h>
+
+unsigned
+do_vlenb_read ()
+{
+ unsigned vlenb;
+ asm volatile ("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb) : :);
+ return vlenb;
+}
+
+void
+reset_vu ()
+{
+ unsigned vl;
+ asm volatile ("vsetvli %[new_vl], x0, e8, m8, ta, ma"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ asm volatile ("vxor.vv v0, v0, v0\n"
+ "vxor.vv v8, v8, v8\n"
+ "vxor.vv v16, v16, v16\n"
+ "vxor.vv v24, v24, v24\n"
+ "vadd.vi v0, v0, 15\n"
+ "vadd.vi v8, v8, 15\n"
+ "vadd.vi v16, v16, 15\n"
+ "vadd.vi v24, v24, 15\n"
+ "csrrsi zero, vxrm, 3\n"
+ "csrrsi zero, vxsat, 1\n");
+ asm volatile ("nop"); /* vu_reset_end */
+}
+
+int
+main ()
+{
+ unsigned vlenb_value = do_vlenb_read ();
+ (void)vlenb_value;
+ reset_vu ();
+ /* vect_test_start */
+ for (int i = 0; i < 777; ++i)
+ reset_vu ();
+ return 0; /* vect_test_end */
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-rwr.exp b/gdb/testsuite/gdb.arch/riscv-vu-rwr.exp
new file mode 100644
index 00000000000..ba8269e6b98
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-rwr.exp
@@ -0,0 +1,163 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+require {istarget "riscv*-*-*"}
+
+if {![riscv_support_rvv]} {
+ unsupported "RVV unsupported"
+ return
+}
+
+standard_testfile
+load_lib riscv64-rvv-lib.exp
+
+proc test_vu_rwr_get_default_vtype_pattern {} {
+ return "0xc3\tLMUL:3 \\(8\\) SEW:0 \\(e8\\) vta:1 \\(ta\\) vma:1 \\(ma\\) vill:0"
+}
+proc test_vu_rwr_get_zero_vtype_pattern {} {
+ return "0x0\tLMUL:0 \\(1\\) SEW:0 \\(e8\\) vta:0 \\(tu\\) vma:0 \\(mu\\) vill:0"
+}
+proc test_vu_rwr_get_default_vl {VLENB} {
+ return [expr $VLENB * 8]
+}
+
+proc test_vu_rwr_is_reg_excluded {excluded reg} {
+ return [expr { [lsearch -exact $excluded $reg] != -1 }]
+}
+
+proc test_vu_rwr_csr_scan { VLENB test_info exclude } {
+ set the_proc [lindex [info level 0] 0]
+ if {![test_vu_rwr_is_reg_excluded $exclude "vtype"]} {
+ set vtype_pattern [test_vu_rwr_get_default_vtype_pattern]
+ gdb_test "info reg vtype" $vtype_pattern "$the_proc: info reg vtype - $test_info"
+ }
+ if {![test_vu_rwr_is_reg_excluded $exclude "vcsr"]} {
+ gdb_test "info reg vcsr" "0x7\tVXSAT:1 VXRM:3" "$the_proc: info reg vcsr - $test_info"
+ }
+ if {![test_vu_rwr_is_reg_excluded $exclude "vl"]} {
+ set vl [test_vu_rwr_get_default_vl $VLENB]
+ gdb_test "info reg vl" "[format 0x%x $vl]\t$vl" "$the_proc: info reg vl - $test_info"
+ }
+ if {![test_vu_rwr_is_reg_excluded $exclude "vstart"]} {
+ gdb_test "info reg vstart" "0x0\t0" "$the_proc: info reg vstart - $test_info"
+ }
+ if {![test_vu_rwr_is_reg_excluded $exclude "vlenb"]} {
+ gdb_test "info reg vlenb" "[format 0x%x $VLENB]\t$VLENB" "$the_proc: info reg vlenb - $test_info"
+ }
+}
+
+proc test_vu_rwr_scan_context {VLENB test_info exclude} {
+ test_vu_rwr_csr_scan $VLENB $test_info $exclude
+ set 15_pattern [riscvlib_rvv_vreg_15_pattern $VLENB]
+ set the_proc [lindex [info level 0] 0]
+
+ for {set i 0} {$i < 32} {incr i} {
+ if { $exclude eq "v$i" } {
+ continue
+ }
+ gdb_test "print \$v$i" ${15_pattern} "$the_proc: print v$i - $test_info"
+ }
+}
+
+proc test_vu_rwr {VLENB} {
+ global srcfile
+
+ set the_proc [lindex [info level 0] 0]
+ set i8_fmt [riscvlib_rvv_vreg_fmt8]
+ test_vu_rwr_scan_context $VLENB "initial-scan" ""
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vu_reset_end]"
+ gdb_continue_to_breakpoint "vu_reset_end"
+
+ for {set i 0} {$i < 32} {incr i} {
+ gdb_continue_to_breakpoint "vu_reset_end - v$i"
+ set vreg_contents {}
+ for { set j 0} {$j < $VLENB } { incr j } {
+ gdb_test_no_output "set \$v$i.${i8_fmt}\[$j\] = $j"
+ lappend vreg_contents $j
+ }
+ test_vu_rwr_scan_context $VLENB "v$i update-scan" "v$i"
+
+ set vreg_pattern [join $vreg_contents ", "]
+ gdb_test "print \$v$i" "\\\{$i8_fmt = \\{$vreg_pattern\\},.+" "print v$i - after modification"
+ }
+
+ gdb_continue_to_breakpoint "vu_reset_end - vtype"
+ gdb_test_no_output "set \$vtype = 0"
+ test_vu_rwr_scan_context $VLENB "vtype update-scan" {vtype vl}
+ set zero_vtype_pattern [test_vu_rwr_get_zero_vtype_pattern]
+ set default_vl [test_vu_rwr_get_default_vl $VLENB]
+ gdb_test "info reg vtype" $zero_vtype_pattern "$the_proc: info reg vtype - csr update"
+ gdb_test "info reg vl" "[format 0x%x $default_vl]\t$default_vl" "$the_proc: info reg vl - vl after vtype update"
+ gdb_test "stepi" ".*" "$the_proc: stepi after vtype update"
+ gdb_test "info reg vtype" $zero_vtype_pattern "$the_proc: info reg vtype - csr update and stepi"
+ gdb_test "info reg vl" "[format 0x%x $default_vl]\t$default_vl" "$the_proc: info reg vl - vl after vtype update and stepi"
+
+ gdb_continue_to_breakpoint "vu_reset_end - vcsr"
+ gdb_test_no_output "set \$vcsr = 0"
+ test_vu_rwr_scan_context $VLENB "vcsr update-scan" "vcsr"
+ gdb_test "info reg vcsr" "0x0\tVXSAT:0 VXRM:0" "$the_proc: info reg vcsr - csr update"
+ gdb_test "stepi" ".*" "$the_proc: stepi after vcsr update"
+ gdb_test "info reg vcsr" "0x0\tVXSAT:0 VXRM:0" "$the_proc: info reg vcsr - after stepi"
+
+ gdb_continue_to_breakpoint "vu_reset_end - vl"
+ gdb_test_no_output "set \$vl = 2"
+ test_vu_rwr_scan_context $VLENB "vl update-scan" "vl"
+ gdb_test "info reg vl" "[format 0x%x 2]\t2" "$the_proc: info reg vl - csr update"
+ gdb_test "stepi" ".*" "$the_proc: stepi after vl update"
+ gdb_test "info reg vl" "0x2\t2" "$the_proc: info reg vl - after stepi"
+
+ gdb_continue_to_breakpoint "vu_reset_end - vstart"
+ gdb_test_no_output "set \$vstart = 2"
+ test_vu_rwr_scan_context $VLENB "vstart update-scan" "vstart"
+ gdb_test "info reg vstart" "0x2\t2" "$the_proc: info reg vstart - vsart update"
+ gdb_test "stepi" ".*" "$the_proc: stepi after vstart update"
+ gdb_test "info reg vstart" "0x2\t2" "$the_proc: info reg vstart - after stepi"
+
+ gdb_continue_to_breakpoint "vu_reset_end - vlenb"
+ gdb_test_no_output "set \$vlenb = 0"
+ test_vu_rwr_scan_context $VLENB "$the_proc: vlenb update-scan" ""
+ gdb_test "stepi" ".*" "$the_proc: stepi after vlenb update"
+ test_vu_rwr_scan_context $VLENB "$the_proc: final scan after stepi" ""
+}
+
+proc prepare_vu_rwr_test {} {
+ global testfile
+ global srcfile
+
+ set compile_flags {}
+ lappend compile_flags debug
+ lappend compile_flags "additional_flags=-march=rv64gc"
+
+ if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+ }
+
+ if {![runto_main]} {
+ return -1
+ }
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_test_start]"
+ gdb_continue_to_breakpoint "vect_test_start"
+
+ return 0
+}
+
+if {[prepare_vu_rwr_test]} {
+ untested "could not initialize"
+ return -1
+}
+set vlenb [riscvlib_rvv_get_csr vlenb "$testfile"]
+test_vu_rwr $vlenb
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-side-effects.c b/gdb/testsuite/gdb.arch/riscv-vu-side-effects.c
new file mode 100644
index 00000000000..138d2895dad
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-side-effects.c
@@ -0,0 +1,86 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+asm (".option arch, +v\n");
+
+#include <stdlib.h>
+#include <limits.h>
+
+unsigned
+do_vlenb_read ()
+{
+ unsigned vlenb;
+ asm volatile ("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb) : :);
+ return vlenb;
+}
+
+char *STORAGE;
+
+void
+zero_out_vu ()
+{
+ unsigned vl;
+ asm volatile ("vsetvli %[new_vl], x0, e8, m8, tu, mu"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ asm volatile ("vxor.vv v0, v0, v0");
+ asm volatile ("vxor.vv v8, v8, v8");
+ asm volatile ("vxor.vv v16, v16, v16");
+ asm volatile ("vxor.vv v24, v24, v24");
+}
+
+void
+do_wide_operations ()
+{
+ unsigned vl;
+ asm volatile ("vsetvli %[new_vl], x0, e8, m8, tu, mu"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ asm volatile ("vadd.vi v0, v0, 0x1"); /* vect_wide_op_start */
+ asm volatile ("vadd.vi v24, v0, 0x2"); /* vect_op_v0_add1 */
+ asm volatile ("vadd.vi v16, v8, 0x2"); /* vect_op_v24_v0_add2 */
+ asm volatile ("vadd.vi v10, v9, 0x3"); /* vect_op_v16_v8_add2 */
+ asm volatile ("nop"); /* vect_wide_op_end */
+}
+
+void
+do_controlled_vadd ()
+{
+ unsigned vl;
+ asm volatile ("vsetvli %[new_vl], x0, e8, m1, tu, mu"
+ : [new_vl] "=r"(vl)
+ :
+ :);
+ asm volatile ("vadd.vv v2, v1, v0"); /* vect_control_vadd_start */
+ asm volatile ("nop"); /* controlled_vadd_done */
+}
+
+int
+main ()
+{
+ unsigned vlenb_value = do_vlenb_read ();
+ STORAGE = (char *)calloc (1, vlenb_value * CHAR_BIT);
+
+ zero_out_vu ();
+ /* vect_test_start */
+ do_controlled_vadd ();
+ zero_out_vu ();
+ do_wide_operations ();
+ return 0; /* vect_test_end */
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-vu-side-effects.exp b/gdb/testsuite/gdb.arch/riscv-vu-side-effects.exp
new file mode 100644
index 00000000000..e96dd421db8
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-vu-side-effects.exp
@@ -0,0 +1,162 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+require {istarget "riscv*-*-*"}
+
+if {![riscv_support_rvv]} {
+ unsupported "RVV unsupported"
+ return
+}
+
+standard_testfile
+load_lib riscv64-rvv-lib.exp
+
+proc test_vu_controlled_add {VLENB} {
+ global srcfile
+
+ set i8_fmt [riscvlib_rvv_vreg_fmt8]
+ set zero_pattern [riscvlib_rvv_vreg_zero_pattern $VLENB]
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_control_vadd_start]"
+ gdb_continue_to_breakpoint "vect_control_vadd_start"
+
+ set the_proc [lindex [info level 0] 0]
+
+ # ensure that state is pristine
+ for { set vregn 0 } { $vregn < 32 } { incr vregn } {
+ gdb_test "print \$v${vregn}" ${zero_pattern} "$the_proc: print v${vregn} - pristine"
+ }
+
+ # update v0
+ for {set i 0} {$i < $VLENB} {incr i} {
+ set val [expr {$i % 256 - 128}]
+ gdb_test_no_output "set \$v0.${i8_fmt}\[$i\] = $val"
+ lappend v0_contents $val
+ }
+
+ # update v1
+ for {set i 0} {$i < $VLENB} {incr i} {
+ set val [expr {($i + 7) % 256 - 128}]
+ gdb_test_no_output "set \$v1.${i8_fmt}\[$i\] = $val"
+ lappend v1_contents $val
+ }
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number controlled_vadd_done]"
+ # execute addition operation, v3 register must be updated to summ of v0 and v1
+ gdb_continue_to_breakpoint "controlled_vadd_done"
+
+ set v0_pattern [join $v0_contents ", "]
+ gdb_test "print \$v0" "\\\{$i8_fmt = \\{$v0_pattern\\},.+" "$the_proc: print v0 - after add"
+
+ set v1_pattern [join $v1_contents ", "]
+ gdb_test "print \$v1" "\\\{$i8_fmt = \\{$v1_pattern\\},.+" "$the_proc: print v1 - after add"
+ for {set i 0} {$i < $VLENB} {incr i} {
+ lappend v2_contents [expr {($i + $i + 7 + 128) % 256 - 128}]
+ }
+ set v2_pattern [join $v2_contents ", "]
+ gdb_test "print \$v2" "\\\{$i8_fmt = \\{$v2_pattern\\},.+" "$the_proc: print v2 - add result"
+ for { set vregn 3 } { $vregn < 32 } { incr vregn } {
+ gdb_test "print \$v${vregn}" ${zero_pattern} "$the_proc: print v${vregn} - pristine after add"
+ }
+}
+
+proc test_vu_wide_operations {VLENB} {
+ global srcfile
+
+ set i8_fmt [riscvlib_rvv_vreg_fmt8]
+ set the_proc [lindex [info level 0] 0]
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_wide_op_start]"
+ gdb_continue_to_breakpoint "vect_wide_op_start"
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_op_v0_add1]"
+ gdb_continue_to_breakpoint "vect_op_v0_add1"
+
+ gdb_test_no_output "set \$vl = 2"
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_op_v24_v0_add2]"
+ gdb_continue_to_breakpoint "vect_op_v24_v0_add2"
+
+ for {set i 0} {$i < $VLENB} {incr i} {
+ gdb_test_no_output "set \$v8.${i8_fmt}\[$i\] = $i"
+ lappend v8_contents $i
+ }
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_op_v16_v8_add2]"
+ gdb_continue_to_breakpoint "vect_op_v16_v8_add2"
+
+ gdb_test_no_output "set \$vtype = 0"
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_wide_op_end]"
+ gdb_continue_to_breakpoint "vect_wide_op_end"
+
+ set vtype_pattern "0x0\tLMUL:0 \\(1\\) SEW:0 \\(e8\\) vta:0 \\(tu\\) vma:0 \\(mu\\) vill:0"
+ gdb_test "info reg vtype" "${vtype_pattern}" "$the_proc: info reg vtype - end state"
+ gdb_test "info reg vcsr" "0x0\tVXSAT:0 VXRM:0" "$the_proc: info reg vcsr - end state"
+ gdb_test "info reg vl" "[format 0x%x 2]\t2" "$the_proc: info reg vl - end state"
+ gdb_test "info reg vstart" "0x0\t0" "$the_proc: info reg vstart - end state"
+ gdb_test "info reg vlenb" "[format 0x%x $VLENB]\t$VLENB" "$the_proc: info reg vlenb - end state"
+
+ set zero_pattern [riscvlib_rvv_vreg_zero_pattern $VLENB]
+ set ones_pattern [riscvlib_rvv_vreg_1_pattern $VLENB]
+ foreach vregn { 0 1 2 3 4 5 6 7 } {
+ gdb_test "print \$v${vregn}" ${ones_pattern} "$the_proc: print v${vregn} - end state"
+ }
+ foreach vregn { 9 11 12 13 14 15 17 18 19 20 21 22 23 25 26 27 28 29 30 31} {
+ gdb_test "print \$v${vregn}" ${zero_pattern} "$the_proc: print v${vregn} - end state"
+ }
+ set xn_zeroes_rep [riscvlib_rvv_vreg_component_pattern [expr {$VLENB - 2}] 0]
+ # for VLENB = 16 we have:
+ # v10 \{i8 = {3, 3, 0 <repeats 14 times>} ...
+ # v16 \{i8 = {2, 3, 0 <repeats 14 times>} ...
+ # v24 \{i8 = {3, 3, 0 <repeats 14 times>} ...
+ foreach vregn { 10 24 } {
+ gdb_test "print \$v${vregn}" "\\\{$i8_fmt = \\{3, 3, ${xn_zeroes_rep}\\},.+" "$the_proc: print v${vregn} - end state"
+ }
+ gdb_test "print \$v16" "\\\{$i8_fmt = \\{2, 3, ${xn_zeroes_rep}\\},.+" "$the_proc: print v16 - end state"
+
+ # v8 \{i8 = {0, 1, 2, ...} ....
+ set v8_pattern [join $v8_contents ", "]
+ gdb_test "print \$v8" "\\\{$i8_fmt = \\{$v8_pattern\\},.+" "$the_proc: print v8 - end state"
+}
+
+proc prepare_vu_rw_test {} {
+ global testfile
+ global srcfile
+
+ set compile_flags {}
+ lappend compile_flags debug
+ lappend compile_flags "additional_flags=-march=rv64gc"
+
+ if {[prepare_for_testing "failed to prepare" $testfile $srcfile $compile_flags]} {
+ return -1
+ }
+
+ if {![runto_main]} {
+ return -1
+ }
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number vect_test_start]"
+ gdb_continue_to_breakpoint "vect_test_start"
+
+ return 0
+}
+
+if {[prepare_vu_rw_test]} {
+ untested "could not initialize"
+ return -1
+}
+set vlenb [riscvlib_rvv_get_csr vlenb "$testfile"]
+test_vu_controlled_add $vlenb
+test_vu_wide_operations $vlenb
diff --git a/gdb/testsuite/lib/riscv64-rvv-lib.exp b/gdb/testsuite/lib/riscv64-rvv-lib.exp
new file mode 100644
index 00000000000..ada5a9c052d
--- /dev/null
+++ b/gdb/testsuite/lib/riscv64-rvv-lib.exp
@@ -0,0 +1,166 @@
+proc riscvlib_is_vlmul_vsew_legal {VLENB vlmul vsew} {
+ # vlmul == 5 => 1/8
+ # vlmul == 6 => 1/4
+ # vlmul == 7 => 1/2
+ set sew [expr {1 << ($vsew + 3)}]
+ if {$vlmul > 4} {
+ set lmul_modifier [expr {1 << (8 - $vlmul)}]
+ set required_vlen [expr {$sew * $lmul_modifier}]
+ set vlen [expr {$VLENB * 8}]
+ return [expr {$vlen >= $required_vlen}];
+ }
+ if {$vlmul < 4} {
+ set lmul_modifier [expr {1 << $vlmul}]
+ set required_vlen $sew
+ set vlen [expr {$VLENB * 8 * $lmul_modifier}]
+ return [expr {$vlen >= $required_vlen}]
+ }
+
+ return 0;
+}
+
+proc riscvlib_get_allowed_vl {VLENB vlmul vsew vl} {
+ # vlmul == 5 => 1/8
+ # vlmul == 6 => 1/4
+ # vlmul == 7 => 1/2
+ # dummy way to filter-out illegal cases
+ if {![riscvlib_is_vlmul_vsew_legal $VLENB $vlmul $vsew]} {
+ return 0
+ }
+ set sew [expr {1 << ($vsew + 3)}]
+ set vlen [expr {$VLENB * 8}]
+ set vlmax 0
+
+ if {$vlmul > 4} {
+ set lmul_modifier [expr {1 << (8 - $vlmul)}]
+ set vlmax [expr {$vlen / ($lmul_modifier * $sew)}]
+ }
+
+ if {$vlmul < 4} {
+ set lmul_modifier [expr {1 << $vlmul}]
+ set vlmax [expr {$vlen * $lmul_modifier / $sew}]
+ }
+
+ if {$vl > $vlmax} {
+ return $vlmax
+ }
+
+ return $vl
+}
+
+proc riscvlib_rvv_get_csr {name test_id} {
+ global hex
+ global decimal
+ global gdb_prompt
+ global gdb_test_name
+
+ gdb_test_multiple "info registers $name" "" {
+ -re "^info registers\[^\r\n\]+\r\n" {
+ exp_continue
+ }
+ -re "^$name\\s+(${hex})\\s+\[^\n]+\r\n" {
+ set value [expr {$expect_out(1,string)}]
+ exp_continue
+ }
+ -re "^$gdb_prompt $" {
+ pass "$gdb_test_name $test_id"
+ }
+ }
+ return $value
+}
+
+proc riscvlib_rvv_vreg_fmt8 {} {
+ return i8
+}
+proc riscvlib_rvv_vreg_fmt16 {} {
+ return i16
+}
+proc riscvlib_rvv_vreg_fmt32 {} {
+ return i32
+}
+proc riscvlib_rvv_vreg_fmt64 {} {
+ return i64
+}
+
+proc riscvlib_rvv_vreg_print_pattern { i8 i16 i32 i64 half f32 f64} {
+ set I8_FMT [riscvlib_rvv_vreg_fmt8]
+ set I16_FMT [riscvlib_rvv_vreg_fmt16]
+ set I32_FMT [riscvlib_rvv_vreg_fmt32]
+ set I64_FMT [riscvlib_rvv_vreg_fmt64]
+ set HALF_FMT half
+ set F32_FMT f32
+ set F64_FMT f64
+ return [join [list \
+ "\\\{${I8_FMT} = \\{$i8\\}" \
+ "${I16_FMT} = \\{$i16\\}" \
+ "${I32_FMT} = \\{$i32\\}" \
+ "${I64_FMT} = \\{$i64\\}" \
+ "${HALF_FMT} = \\{$half\\}" \
+ "${F32_FMT} = \\{$f32\\}" \
+ "${F64_FMT} = \\{$f64\\}\\\}" \
+ ] ", "]
+}
+
+proc riscvlib_rvv_vreg_component_pattern {repeat_count symbol {collapse_allowed 1}} {
+ if { $collapse_allowed } {
+ if { $repeat_count > 8 } {
+ return "$symbol <repeats $repeat_count times>"
+ }
+ }
+ return [join [lrepeat $repeat_count $symbol] ", "]
+}
+
+proc riscvlib_rvv_vreg_zero_pattern {vlenb} {
+ set zero_symbol 0
+ set pattern [list \
+ [riscvlib_rvv_vreg_component_pattern $vlenb $zero_symbol] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 2 }] $zero_symbol] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 4 }] $zero_symbol] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 8 }] $zero_symbol] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 2 }] $zero_symbol] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 4 }] $zero_symbol] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 8 }] $zero_symbol] \
+ ]
+ return [riscvlib_rvv_vreg_print_pattern {*}$pattern]
+}
+
+proc riscvlib_rvv_vreg_1_pattern {vlenb} {
+ set pattern [list \
+ [riscvlib_rvv_vreg_component_pattern $vlenb 1] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 2 }] 257] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 4 }] 16843009] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 8 }] 72340172838076673] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 2 }] 1.5318e-05] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 4 }] 2.36942783e-38] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 8 }] 7.7486041854893479e-304] \
+ ]
+ return [riscvlib_rvv_vreg_print_pattern {*}$pattern]
+}
+
+proc riscvlib_rvv_vreg_3_pattern {vlenb} {
+ set zero_symbol 0
+ set pattern [list \
+ [riscvlib_rvv_vreg_component_pattern $vlenb 3] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 2 }] 771] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 4 }] 50529027] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 8 }] 217020518514230019] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 2 }] 4.5955e-05] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 4 }] 3.85008973e-37] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 8 }] 3.7209743448696002e-294] \
+ ]
+ return [riscvlib_rvv_vreg_print_pattern {*}$pattern]
+}
+
+proc riscvlib_rvv_vreg_15_pattern {vlenb} {
+ set zero_symbol 0
+ set pattern [list \
+ [riscvlib_rvv_vreg_component_pattern $vlenb 15] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 2 }] 3855] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 4 }] 252645135] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 8 }] 1085102592571150095] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 2 }] 0.00043082] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 4 }] 7.05334452e-30] \
+ [riscvlib_rvv_vreg_component_pattern [expr { $vlenb / 8 }] 3.8157368271180168e-236] \
+ ]
+ return [riscvlib_rvv_vreg_print_pattern {*}$pattern]
+}
--
2.43.0
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-11-07 17:02 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-07 16:55 [PATCH 1/2] RISC-V Vector Extension Support Kirill Radkin
2025-11-07 16:55 ` [PATCH 2/2] RISC-V Vector Extension Support Testing Kirill Radkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox