From: Kirill Radkin <hokeygo@gmail.com>
To: gdb-patches@sourceware.org
Cc: Peter Bergner <bergner@oss.tenstorrent.com>,
Sergey Matyukevich <geomatsi@gmail.com>,
Jerry Zhang Jian <jerry.zhangjian@sifive.com>,
Heinrich Schuchardt <heinrich.schuchardt@canonical.com>,
Andrew Burgess <aburgess@redhat.com>,
Palmer Dabbelt <palmer@dabbelt.com>
Subject: [PATCH v2 1/3] RISC-V Vector Extension Support
Date: Fri, 25 Sep 2026 20:29:45 +0300 [thread overview]
Message-ID: <20260925172947.403655-2-hokeygo@gmail.com> (raw)
In-Reply-To: <20260925172947.403655-1-hokeygo@gmail.com>
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 (64, 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 | 35 ++
gdb/riscv-linux-nat.c | 164 +++++++
gdb/riscv-regs.h | 79 ++++
gdb/riscv-tdep.c | 876 ++++++++++++++++++++++++++++++++---
gdb/riscv-tdep.h | 63 +--
gdbserver/linux-riscv-low.cc | 129 ++++--
gdbsupport/common-utils.h | 22 +
12 files changed, 1422 insertions(+), 158 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 c0e6c6aec63..96a68bf1654 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 1443bc20a7d..0261d4d0f9e 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..060cb5a44f2
--- /dev/null
+++ b/gdb/features/riscv/rvv.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 2026 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 4b6c01910f4..34581d65bb4 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1977,6 +1977,21 @@ is_dynamic_type_internal_1 (struct type *type,
}
}
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_1 (type->target_type (), false))
+ return true;
+
+ /* Same for function arguments */
+ for (int i = 0; i < type->num_fields (); ++i)
+ if (is_dynamic_type_internal_1 (type->field (i).type (), false))
+ return true;
+ }
+ break;
}
return false;
@@ -2742,6 +2757,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 *
@@ -2840,6 +2874,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..f021b263844
--- /dev/null
+++ b/gdb/nat/riscv-linux-ptrace.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2026 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 1cb3129ba6d..a3e0fe383bb 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 in Linux. */
+#define RISCV_HWPROBE_EXT_ZVE32X (1ULL << 37)
+#endif
+
+struct riscv_hwprobe
+{
+ int64_t key;
+ uint64_t value;
+};
+
/* See nat/riscv-linux-tdesc.h. */
struct riscv_gdbarch_features
@@ -78,5 +99,19 @@ riscv_linux_read_features (int tid)
break;
}
+ features.vlenb = 0;
+
+ static struct riscv_hwprobe query[] = { { RISCV_HWPROBE_KEY_IMA_EXT_0, 0 } };
+
+ /* All possible vector extensions depends on Zve32x, and it's availability is
+ a minimum requirement. */
+ if ((syscall (NR_riscv_hwprobe, query, 1, 0, NULL, 0) == 0)
+ && (query[0].value & RISCV_HWPROBE_EXT_ZVE32X))
+ {
+ 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 e1e81ad50c4..aecb777b7ce 100644
--- a/gdb/riscv-linux-nat.c
+++ b/gdb/riscv-linux-nat.c
@@ -24,6 +24,7 @@
#include "elf/common.h"
+#include "nat/riscv-linux-ptrace.h"
#include "nat/riscv-linux-tdesc.h"
#include <sys/ptrace.h>
@@ -132,6 +133,64 @@ 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 +254,64 @@ 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..76810d3ef4b
--- /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) 2026 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 100a44cddf3..12bc51beb38 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -57,6 +57,12 @@
#include "record-full.h"
#include "riscv-ravenscar-thread.h"
+#include <array>
+#include <charconv>
+#include <list>
+#include <numeric>
+#include <optional>
+#include <regex>
#include <vector>
/* The stack must be 16-byte aligned. */
@@ -660,10 +666,16 @@ 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 +691,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 +708,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 +727,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 +808,58 @@ 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 the target for GDBARCH has vector hardware. */
+
+static bool
+riscv_has_vector_regs (struct gdbarch *gdbarch)
+{
+ return (riscv_isa_vlenb (gdbarch) > 0);
+}
+
+/* Return true if REGNO is a vector GPR. */
+
+static bool
+riscv_is_vector_gpr (int regno)
+{
+ return (regno >= RISCV_V0_REGNUM && regno <= RISCV_V31_REGNUM);
+}
+
+/* Return true if REGNO is a vector CSR. */
+
+static bool
+riscv_is_vector_rw_csr (int regno)
+{
+ return (regno == RISCV_CSR_VSTART_REGNUM || regno == RISCV_CSR_VCSR_REGNUM
+ || regno == RISCV_CSR_VL_REGNUM || regno == RISCV_CSR_VTYPE_REGNUM);
+}
+
+/* Return true if REGNO is a vector GPR or CSR. */
+
+static bool
+riscv_is_vector_save_restore_regno (int regno)
+{
+ return riscv_is_vector_gpr (regno) || riscv_is_vector_rw_csr (regno);
+}
+
+/* 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)
{
@@ -1052,7 +1119,7 @@ riscv_pseudo_register_write (struct gdbarch *gdbarch,
static bool
riscv_cannot_store_register (struct gdbarch *gdbarch, int regnum)
{
- return regnum == RISCV_ZERO_REGNUM;
+ return regnum == RISCV_ZERO_REGNUM || regnum == RISCV_CSR_VLENB_REGNUM;
}
/* Construct a type for 64-bit FP registers. */
@@ -1200,7 +1267,7 @@ riscv_print_one_register_info (struct gdbarch *gdbarch,
{
riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
- /* Print the register in hex. */
+ /* Print the register in hex, exclude vector registers. */
value_print_options opts = get_formatted_print_options ('x');
opts.deref_ref = true;
common_val_print (val, file, 0, opts, current_language);
@@ -1330,17 +1397,46 @@ 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)
- {
- opts = get_user_print_options ();
- 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)
+ {
+ opts = get_user_print_options ();
+ opts.deref_ref = true;
+ gdb_printf (file, "\t");
+ common_val_print (val, file, 0, opts, current_language);
}
}
}
@@ -1433,21 +1529,22 @@ riscv_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
return false;
}
else if (reggroup == float_reggroup)
- return (riscv_is_fp_regno_p (regnum)
- || regnum == RISCV_CSR_FCSR_REGNUM
- || regnum == tdep->fflags_regnum
- || regnum == tdep->frm_regnum);
+ return (riscv_is_fp_regno_p (regnum) || regnum == RISCV_CSR_FCSR_REGNUM
+ || regnum == tdep->fflags_regnum || regnum == tdep->frm_regnum);
else if (reggroup == general_reggroup)
return regnum < RISCV_FIRST_FP_REGNUM;
else if (reggroup == restore_reggroup || reggroup == save_reggroup)
{
if (riscv_has_fp_regs (gdbarch))
- return (regnum <= RISCV_LAST_FP_REGNUM
- || regnum == RISCV_CSR_FCSR_REGNUM
- || regnum == tdep->fflags_regnum
- || regnum == tdep->frm_regnum);
- else
- return regnum < RISCV_FIRST_FP_REGNUM;
+ if (riscv_is_fp_regno_p (regnum) || regnum == RISCV_CSR_FCSR_REGNUM
+ || regnum == tdep->fflags_regnum || regnum == tdep->frm_regnum)
+ return 1;
+
+ if (riscv_has_vector_regs (gdbarch)
+ && riscv_is_vector_save_restore_regno (regnum))
+ return 1;
+
+ return regnum < RISCV_FIRST_FP_REGNUM;
}
else if (reggroup == system_reggroup || reggroup == csr_reggroup)
{
@@ -1460,7 +1557,7 @@ riscv_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
return false;
}
else if (reggroup == vector_reggroup)
- return (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM);
+ return riscv_is_vpr_or_vcsr (regnum);
else
return false;
}
@@ -2645,18 +2742,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 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 as an on stack argument. */
- on_stack,
+ /* 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;
+ /* 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
@@ -2712,6 +2816,90 @@ struct riscv_arg_reg
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
@@ -2749,10 +2937,16 @@ 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)
+ 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). */
@@ -2762,6 +2956,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
@@ -2776,10 +2974,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
@@ -2821,6 +3024,410 @@ 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. */
+ enum class rvv_sew_t : int
+ {
+ SEW8 = 8,
+ SEW16 = 16,
+ SEW32 = 32,
+ SEW64 = 64,
+ SEW_UNKNOWN,
+ } sew = rvv_sew_t::SEW_UNKNOWN;
+
+ /* Length Multiplier, amount of registers used for this type. */
+ enum class rvv_lmul_t : int
+ {
+ LMUL1 = 1,
+ LMUL2 = 2,
+ LMUL4 = 4,
+ LMUL8 = 8,
+ LMUL_UNKNOWN,
+ } lmul = rvv_lmul_t::LMUL_UNKNOWN;
+
+ /* For non-tuple types, nfield = 1. */
+ enum class rvv_nfield_t : int
+ {
+ NFIELD1 = 1,
+ NFIELD2,
+ NFIELD3,
+ NFIELD4,
+ NFIELD5,
+ NFIELD6,
+ NFIELD7,
+ NFIELD8,
+ NFIELD_UNKNOWN,
+ } nfield = rvv_nfield_t::NFIELD_UNKNOWN;
+
+ enum class rvv_elem_t : char
+ {
+ RVV_INT,
+ RVV_UINT,
+ RVV_FLOAT,
+ RVV_BOOL,
+ RVV_UNKNOWN,
+ } element_type = rvv_elem_t::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;
+};
+
+struct rvv_type_name_mapping_t
+{
+ const char *name;
+ rvv_type_info::rvv_elem_t type;
+};
+
+static constexpr std::array<rvv_type_name_mapping_t, 4> rvv_type_names = {
+ rvv_type_name_mapping_t { "int", rvv_type_info::rvv_elem_t::RVV_INT },
+ rvv_type_name_mapping_t { "uint", rvv_type_info::rvv_elem_t::RVV_UINT },
+ rvv_type_name_mapping_t { "float", rvv_type_info::rvv_elem_t::RVV_FLOAT },
+ rvv_type_name_mapping_t { "bool", rvv_type_info::rvv_elem_t::RVV_BOOL }
+};
+
+static bool
+supported_fractional_sew_lmul (rvv_type_info::rvv_lmul_t lmul,
+ rvv_type_info::rvv_sew_t sew)
+{
+ gdb_assert (lmul != rvv_type_info::rvv_lmul_t::LMUL_UNKNOWN);
+ gdb_assert (sew != rvv_type_info::rvv_sew_t::SEW_UNKNOWN);
+ /* More info in RISC-V C Intrinsic Specification Type
+ * System 7.1, 7.2, 7.3, 7.4. */
+ return static_cast<int> (lmul) * static_cast<int> (sew) <= 64;
+}
+
+/* verify_rvv_type() verify RVV types according to specification. */
+static bool
+verify_rvv_type (const rvv_type_info &type_info)
+{
+ if (type_info.lmul == rvv_type_info::rvv_lmul_t::LMUL1
+ && type_info.is_fractional_lmul)
+ return false;
+
+ /* Restriction from RISC-V Vector Specification: LMUL * NFIELDS <= 8. */
+ if (!type_info.is_fractional_lmul
+ && (static_cast<int> (type_info.lmul)
+ * static_cast<int> (type_info.nfield)
+ > 8))
+ return false;
+
+ switch (type_info.element_type)
+ {
+ case rvv_type_info::rvv_elem_t::RVV_FLOAT:
+ /* For float types at least SEW16 is required. */
+ if (type_info.sew != rvv_type_info::rvv_sew_t::SEW16
+ && type_info.sew != rvv_type_info::rvv_sew_t::SEW32
+ && type_info.sew != rvv_type_info::rvv_sew_t::SEW64)
+ return false;
+
+ /* For float types at least LMUL = 1/4 is required. */
+ if (type_info.lmul == rvv_type_info::rvv_lmul_t::LMUL8
+ && type_info.is_fractional_lmul)
+ return false;
+
+ /* rvv_spec_requirement condition applies only to types with fractional
+ LMUL. */
+ if (!type_info.is_fractional_lmul)
+ return true;
+
+ return supported_fractional_sew_lmul (type_info.lmul, type_info.sew);
+
+ case rvv_type_info::rvv_elem_t::RVV_INT:
+ case rvv_type_info::rvv_elem_t::RVV_UINT:
+ /* For int/uint types at least SEW8 is required. */
+ if (type_info.sew != rvv_type_info::rvv_sew_t::SEW8
+ && type_info.sew != rvv_type_info::rvv_sew_t::SEW16
+ && type_info.sew != rvv_type_info::rvv_sew_t::SEW32
+ && type_info.sew != rvv_type_info::rvv_sew_t::SEW64)
+ return false;
+
+ /* rvv_spec_requirement condition applies only to types with fractional
+ LMUL. */
+ if (!type_info.is_fractional_lmul)
+ return true;
+
+ return supported_fractional_sew_lmul (type_info.lmul, type_info.sew);
+
+ case rvv_type_info::rvv_elem_t::RVV_BOOL:
+ /* These restrictions do not follow from the specification, but from the
+ internal constraints of the structure of rvv_type_info. */
+ return (type_info.lmul == rvv_type_info::rvv_lmul_t::LMUL1
+ && type_info.nfield == rvv_type_info::rvv_nfield_t::NFIELD1
+ && !type_info.is_fractional_lmul);
+
+ default:
+ return false;
+ }
+}
+
+static std::regex
+get_rvv_type_regex ()
+{
+ /* According to specification, RVV type names don't start with __rvv_ (like
+ vint64m1_t), but in GCC and Clang they are aliases (__rvv_int64m1_t). */
+ auto pipe_fold = [] (const std::string &a,
+ const rvv_type_name_mapping_t &b) {
+ return a + "|" + std::string (b.name);
+ };
+ std::string accumulator
+ = std::accumulate (std::next (rvv_type_names.begin ()),
+ rvv_type_names.end (),
+ std::string (rvv_type_names[0].name), pipe_fold);
+ return std::regex ("__rvv_(" + accumulator
+ + ")(1|2|4|8|16|32|64)((m|mf)([1248]))?(x([2-8]))?_t");
+}
+
+struct parsed_rvv_type_t
+{
+ enum class group
+ {
+ name = 1,
+ sew,
+ lmul_str,
+ lmul_type,
+ lmul_val,
+ nfield_str,
+ nfield_val
+ };
+
+ static std::optional<parsed_rvv_type_t> parse (std::string_view name)
+ {
+ parsed_rvv_type_t res;
+ if (std::regex_search (name.begin (), name.end (), res.matches,
+ rvv_type_regex))
+ return res;
+ return std::nullopt;
+ }
+
+ std::string_view get (group g) const
+ {
+ long unsigned index = static_cast<long unsigned> (g);
+ const auto &sm = matches[index];
+ return std::string_view (sm.first, sm.length ());
+ }
+
+private:
+ parsed_rvv_type_t () = default;
+ std::cmatch matches;
+ inline static const std::regex rvv_type_regex = get_rvv_type_regex ();
+};
+
+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;
+ }
+
+ auto parsed_rvv_type_holder = parsed_rvv_type_t::parse (type->name ());
+ if (!parsed_rvv_type_holder.has_value ())
+ {
+ riscv_infcall_debug_printf ("Failed to match RVV type name: %s",
+ type->name ());
+ return std::nullopt;
+ }
+ parsed_rvv_type_t parsed_rvv_type = parsed_rvv_type_holder.value ();
+ rvv_type_info res;
+
+ auto it = std::find_if (
+ rvv_type_names.begin (), rvv_type_names.end (),
+ [parsed_rvv_type] (const rvv_type_name_mapping_t &type_name) {
+ return parsed_rvv_type.get (parsed_rvv_type_t::group::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 (auto sew_val = parse_integer<unsigned> (
+ parsed_rvv_type.get (parsed_rvv_type_t::group::sew)))
+ {
+ res.sew = static_cast<rvv_type_info::rvv_sew_t> (sew_val.value ());
+ }
+ else
+ {
+ std::string_view sew_str
+ = parsed_rvv_type.get (parsed_rvv_type_t::group::sew);
+ riscv_infcall_debug_printf (
+ "Failed to convert SEW string (%.*s) to unsigned",
+ static_cast<int> (sew_str.size ()), sew_str.data ());
+ return std::nullopt;
+ }
+
+ if (!parsed_rvv_type.get (parsed_rvv_type_t::group::lmul_str).empty ())
+ {
+ if (parsed_rvv_type.get (parsed_rvv_type_t::group::lmul_type)
+ .compare ("mf")
+ == 0)
+ res.is_fractional_lmul = true;
+ else
+ res.is_fractional_lmul = false;
+
+ if (auto lmul_val = parse_integer<unsigned> (
+ parsed_rvv_type.get (parsed_rvv_type_t::group::lmul_val)))
+ {
+ res.lmul
+ = static_cast<rvv_type_info::rvv_lmul_t> (lmul_val.value ());
+ }
+ else
+ {
+ std::string_view lmul_str
+ = parsed_rvv_type.get (parsed_rvv_type_t::group::lmul_val);
+ riscv_infcall_debug_printf (
+ "Failed to convert LMUL string (%.*s) to unsigned",
+ static_cast<int> (lmul_str.size ()), lmul_str.data ());
+ return std::nullopt;
+ }
+ }
+ else
+ {
+ res.lmul = rvv_type_info::rvv_lmul_t::LMUL1;
+ res.is_fractional_lmul = false;
+ }
+
+ if (!parsed_rvv_type.get (parsed_rvv_type_t::group::nfield_str).empty ())
+ {
+ if (auto nfield_val = parse_integer<unsigned> (
+ parsed_rvv_type.get (parsed_rvv_type_t::group::nfield_val)))
+ {
+ res.nfield
+ = static_cast<rvv_type_info::rvv_nfield_t> (nfield_val.value ());
+ }
+ else
+ {
+ std::string_view nfield_str
+ = parsed_rvv_type.get (parsed_rvv_type_t::group::nfield_val);
+ riscv_infcall_debug_printf (
+ "Failed to convert NFIELD string (%.*s) to unsigned",
+ static_cast<int> (nfield_str.size ()), nfield_str.data ());
+ return std::nullopt;
+ }
+ }
+ else
+ res.nfield = rvv_type_info::rvv_nfield_t::NFIELD1;
+
+ 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),
+ static_cast<int> (arg_type_info.sew),
+ static_cast<int> (arg_type_info.lmul),
+ (arg_type_info.is_fractional_lmul ? "is" : "is not"),
+ static_cast<int> (arg_type_info.nfield));
+
+ int num_required_regs = ((arg_type_info.is_fractional_lmul)
+ ? 1
+ : static_cast<int> (arg_type_info.lmul))
+ * static_cast<int> (arg_type_info.nfield);
+
+ if (arg_type_info.is_fractional_lmul)
+ {
+ num_required_regs = static_cast<int> (arg_type_info.nfield);
+ data_length = data_length / static_cast<int> (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,
+ static_cast<int> (arg_type_info.nfield));
+ }
+ else if (ainfo->type->code () == TYPE_CODE_STRUCT)
+ {
+ first_regnum
+ = reg->get_interval_start (num_required_regs,
+ static_cast<int> (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.
@@ -3229,21 +3836,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 ();
@@ -3253,6 +3883,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:
@@ -3375,6 +4011,14 @@ 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");
}
@@ -3389,15 +4033,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. */
@@ -3437,16 +4079,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 ();
}
@@ -3458,13 +4105,17 @@ riscv_push_dummy_call (struct gdbarch *gdbarch,
{
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_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];
@@ -3538,6 +4189,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");
}
@@ -3568,6 +4235,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
@@ -3606,9 +4275,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)
{
@@ -3761,6 +4435,38 @@ 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"));
@@ -3795,6 +4501,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;
@@ -3923,6 +4630,17 @@ static const struct frame_unwind_legacy riscv_frame_unwind (
/*.prev_arch =*/ NULL
);
+static bool
+riscv_search_extension_in_march (std::string_view march,
+ std::string_view extension)
+{
+ std::string regex_string = "_" + std::string (extension)
+ + "(_|$|[1-9]?\\d+p[1-9]?\\d)";
+ std::regex regexp_for_extension (regex_string);
+ return std::regex_search (march.begin (), march.end (),
+ regexp_for_extension);
+}
+
/* Extract a set of required target features out of ABFD. If ABFD is
nullptr then a RISCV_GDBARCH_FEATURES is returned in its default state. */
@@ -3963,6 +4681,31 @@ 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_view march_str (march);
+ if (riscv_search_extension_in_march (march_str, "v"))
+ features.vlenb = 16;
+ else if (riscv_search_extension_in_march (march_str, "zve64x")
+ || riscv_search_extension_in_march (march_str, "zve64f")
+ || riscv_search_extension_in_march (march_str, "zve64d"))
+ features.vlenb = 8;
+ else if (riscv_search_extension_in_march (march_str, "zve32x")
+ || riscv_search_extension_in_march (march_str, "zve32f"))
+ features.vlenb = 4;
+ else
+ features.vlenb = 0;
+ }
+ else
+ features.vlenb = 0;
}
return features;
@@ -4265,6 +5008,13 @@ riscv_gdbarch_init (struct gdbarch_info info,
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;
@@ -5468,3 +6218,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 344a995a680..a6fd89c9646 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 8cc0a980fc1..4f26c3f6189 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,23 +166,74 @@ 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;
+
+ struct __riscv_v_regset_state *vecregs
+ = (struct __riscv_v_regset_state *) buf;
+ unsigned long vlenb = vecregs->vlenb;
+ gdb_assert (vlenb > 0);
+ int v0_regno = find_regno (tdesc, "v0");
+ 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;
+
+ const struct __riscv_v_regset_state *vecregs
+ = (const struct __riscv_v_regset_state *) buf;
+ unsigned long vlenb = vecregs->vlenb;
+ gdb_assert (vlenb > 0);
+ int v0_regno = find_regno (tdesc, "v0");
+ int v0_regsize = register_size (tdesc, v0_regno);
+ gdb_assert (vlenb == v0_regsize);
+ 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_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 },
+ 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 },
+ 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 },
+ 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
};
@@ -229,6 +261,45 @@ 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)
+{
+ vector_regset_info->size
+ = (features->vlenb
+ ? sizeof (struct __riscv_v_regset_state) + 32 * features->vlenb
+ : 0);
+}
+
+/* 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 de83a715ac4..5f91dbeb3b7 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
@@ -279,4 +281,24 @@ struct string_view_hash
} /* namespace gdb */
+/* Parse decimal integer number from string_view. */
+template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+std::optional<T>
+parse_integer (std::string_view str) noexcept
+{
+ if (str.empty ())
+ return std::nullopt;
+
+ T value {};
+ const char *first = str.data ();
+ const char *last = str.data () + str.size ();
+
+ auto [ptr, ec] = std::from_chars (first, last, value, 10);
+
+ if (ec != std::errc {} || ptr != last)
+ return std::nullopt;
+
+ return value;
+}
+
#endif /* GDBSUPPORT_COMMON_UTILS_H */
--
2.43.0
next prev parent reply other threads:[~2026-09-25 17:31 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-25 17:29 [PATCH v2 0/3] RISC-V Vector Extension support Kirill Radkin
2026-09-25 17:29 ` Kirill Radkin [this message]
2026-09-25 17:29 ` [PATCH v2 2/3] Reverse execution support for RISC-V Vector Extension Kirill Radkin
2026-09-25 17:29 ` [PATCH v2 3/3] RISC-V Vector Extension Support Testing Kirill Radkin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260925172947.403655-2-hokeygo@gmail.com \
--to=hokeygo@gmail.com \
--cc=aburgess@redhat.com \
--cc=bergner@oss.tenstorrent.com \
--cc=gdb-patches@sourceware.org \
--cc=geomatsi@gmail.com \
--cc=heinrich.schuchardt@canonical.com \
--cc=jerry.zhangjian@sifive.com \
--cc=palmer@dabbelt.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox