[llvm-branch-commits] [lldb] [lldb][RISCV] Support RVV register access (PR #184308)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Mar 3 01:55:36 PST 2026
https://github.com/daniilavdeev created https://github.com/llvm/llvm-project/pull/184308
Support RISC-V vector register context (2/3)
Add support for reading and writing RISC-V vector (RVV) registers through the native register context on Linux. This enables LLDB to access all 32 vector registers (v0–v31) and the vector CSR registers during debugging sessions.
>From c5dbaa80b06f797faf279008b3a47d918cb92ab6 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniilavdeev237 at gmail.com>
Date: Wed, 12 Nov 2025 02:56:52 +0000
Subject: [PATCH] [lldb][RISCV] Support RVV register access
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Support RISC-V vector register context (2/3)
Add support for reading and writing RISC-V vector (RVV) registers
through the native register context on Linux. This enables LLDB to
access all 32 vector registers (v0–v31) and the vector CSR registers
during debugging sessions.
---
.../NativeRegisterContextLinux_riscv64.cpp | 135 +++++++++++++++++-
.../NativeRegisterContextLinux_riscv64.h | 13 ++
.../Utility/RegisterContextPOSIX_riscv64.cpp | 4 +
.../Utility/RegisterContextPOSIX_riscv64.h | 2 +
.../Utility/RegisterInfoPOSIX_riscv64.cpp | 48 ++++++-
.../Utility/RegisterInfoPOSIX_riscv64.h | 38 ++++-
6 files changed, 231 insertions(+), 9 deletions(-)
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp
index 45b6c8ff9905b..22fbb9b52e04d 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp
@@ -24,14 +24,54 @@
// System includes - They have to be included after framework includes because
// they define some macros which collide with variable names in other modules
#include <sys/ptrace.h>
+#include <sys/syscall.h>
#include <sys/uio.h>
+#include <unistd.h>
// NT_PRSTATUS and NT_FPREGSET definition
#include <elf.h>
+#ifndef NT_RISCV_VECTOR
+#define NT_RISCV_VECTOR 0x901
+#endif
+#ifndef __NR_riscv_hwprobe
+#define __NR_riscv_hwprobe 258
+#endif
+#ifndef RISCV_HWPROBE_KEY_IMA_EXT_0
+#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
+#endif
+#ifndef RISCV_HWPROBE_IMA_V
+#define RISCV_HWPROBE_IMA_V (1 << 2)
+#endif
+
+struct HWProbeRISCV {
+ int64_t key;
+ uint64_t value;
+};
+
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::process_linux;
+static uint64_t GetVLENB() {
+ struct HWProbeRISCV query = {RISCV_HWPROBE_KEY_IMA_EXT_0, 0};
+ if (syscall(__NR_riscv_hwprobe, &query, 1, 0, NULL, 0) != 0)
+ return 0;
+
+ if ((query.value & RISCV_HWPROBE_IMA_V) == 0)
+ return 0;
+
+ uint64_t vlenb = 0;
+ asm volatile("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb));
+ return vlenb;
+}
+
+static RegisterInfoPOSIX_riscv64::VPR CreateVPRBuffer() {
+ uint64_t vlenb = GetVLENB();
+ if (vlenb > 0)
+ return RegisterInfoPOSIX_riscv64::VPR(vlenb);
+ return RegisterInfoPOSIX_riscv64::VPR();
+}
+
std::unique_ptr<NativeRegisterContextLinux>
NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
const ArchSpec &target_arch, NativeThreadLinux &native_thread) {
@@ -52,8 +92,10 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
opt_regsets.Set(RegisterInfoPOSIX_riscv64::eRegsetMaskFP);
}
- auto register_info_up =
- std::make_unique<RegisterInfoPOSIX_riscv64>(target_arch, opt_regsets);
+ uint64_t vlenb = GetVLENB();
+
+ auto register_info_up = std::make_unique<RegisterInfoPOSIX_riscv64>(
+ target_arch, opt_regsets, vlenb);
return std::make_unique<NativeRegisterContextLinux_riscv64>(
target_arch, native_thread, std::move(register_info_up));
}
@@ -72,12 +114,13 @@ NativeRegisterContextLinux_riscv64::NativeRegisterContextLinux_riscv64(
std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info_up)
: NativeRegisterContextRegisterInfo(native_thread,
register_info_up.release()),
- NativeRegisterContextLinux(native_thread) {
+ NativeRegisterContextLinux(native_thread), m_vpr(CreateVPRBuffer()) {
::memset(&m_fpr, 0, sizeof(m_fpr));
::memset(&m_gpr, 0, sizeof(m_gpr));
m_gpr_is_valid = false;
m_fpu_is_valid = false;
+ m_vpr_is_valid = false;
}
const RegisterInfoPOSIX_riscv64 &
@@ -144,6 +187,13 @@ NativeRegisterContextLinux_riscv64::ReadRegister(const RegisterInfo *reg_info,
offset = CalculateFprOffset(reg_info);
assert(offset < GetFPRSize());
src = (uint8_t *)GetFPRBuffer() + offset;
+ } else if (IsVPR(reg)) {
+ error = ReadVPR();
+ if (error.Fail())
+ return error;
+
+ offset = reg_info->byte_offset;
+ src = static_cast<uint8_t *>(GetVPRBuffer()) + offset;
} else
return Status::FromErrorString(
"failed - register wasn't recognized to be a GPR or an FPR, "
@@ -198,6 +248,16 @@ Status NativeRegisterContextLinux_riscv64::WriteRegister(
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
return WriteFPR();
+ } else if (IsVPR(reg)) {
+ error = ReadVPR();
+ if (error.Fail())
+ return error;
+
+ offset = reg_info->byte_offset;
+ dst = static_cast<uint8_t *>(GetVPRBuffer()) + offset;
+ ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
+
+ return WriteVPR();
}
return Status::FromErrorString("Failed to write register value");
@@ -219,11 +279,21 @@ Status NativeRegisterContextLinux_riscv64::ReadAllRegisterValues(
return error;
}
+ if (GetRegisterInfo().IsVPPresent()) {
+ error = ReadVPR();
+ if (error.Fail())
+ return error;
+ }
+
uint8_t *dst = const_cast<uint8_t *>(data_sp->GetBytes());
::memcpy(dst, GetGPRBuffer(), GetGPRSize());
dst += GetGPRSize();
- if (GetRegisterInfo().IsFPPresent())
+ if (GetRegisterInfo().IsFPPresent()) {
::memcpy(dst, GetFPRBuffer(), GetFPRSize());
+ dst += GetFPRSize();
+ }
+ if (GetRegisterInfo().IsVPPresent())
+ ::memcpy(dst, GetVPRBuffer(), GetVPRSize());
return error;
}
@@ -270,6 +340,16 @@ Status NativeRegisterContextLinux_riscv64::WriteAllRegisterValues(
error = WriteFPR();
if (error.Fail())
return error;
+
+ src += GetFPRSize();
+ }
+
+ if (GetRegisterInfo().IsVPPresent()) {
+ ::memcpy(GetVPRBuffer(), src, GetVPRSize());
+
+ error = WriteVPR();
+ if (error.Fail())
+ return error;
}
return error;
@@ -279,6 +359,8 @@ size_t NativeRegisterContextLinux_riscv64::GetRegContextSize() {
size_t size = GetGPRSize();
if (GetRegisterInfo().IsFPPresent())
size += GetFPRSize();
+ if (GetRegisterInfo().IsVPPresent())
+ size += GetVPRSize();
return size;
}
@@ -291,6 +373,10 @@ bool NativeRegisterContextLinux_riscv64::IsFPR(unsigned reg) const {
return GetRegisterInfo().IsFPReg(reg);
}
+bool NativeRegisterContextLinux_riscv64::IsVPR(unsigned reg) const {
+ return GetRegisterInfo().IsVPReg(reg);
+}
+
Status NativeRegisterContextLinux_riscv64::ReadGPR() {
Status error;
@@ -355,9 +441,50 @@ Status NativeRegisterContextLinux_riscv64::WriteFPR() {
return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
}
+Status NativeRegisterContextLinux_riscv64::ReadVPR() {
+ if (m_vpr_is_valid)
+ return Status();
+
+ struct iovec ioVec;
+ ioVec.iov_base = GetVPRBuffer();
+ ioVec.iov_len = GetVPRSize();
+
+ Status error = ReadRegisterSet(&ioVec, GetVPRSize(), NT_RISCV_VECTOR);
+ if (error.Fail())
+ return error;
+
+ // Additionally check the vlenb value. Due to bugs in early versions of
+ // RVV support in the Linux kernel, it was possible to obtain an invalid
+ // vector register context even if the PTRACE_GETREGSET call succeeded.
+ bool is_valid_ctx =
+ GetVPRBuffer() &&
+ static_cast<RegisterInfoPOSIX_riscv64::VPR::RawVPR *>(GetVPRBuffer())
+ ->vlenb > 0;
+ if (!is_valid_ctx)
+ return Status::FromErrorString("Invalid vector register context");
+
+ m_vpr_is_valid = true;
+ return Status();
+}
+
+Status NativeRegisterContextLinux_riscv64::WriteVPR() {
+ Status error = ReadVPR();
+ if (error.Fail())
+ return error;
+
+ struct iovec ioVec;
+ ioVec.iov_base = GetVPRBuffer();
+ ioVec.iov_len = GetVPRSize();
+
+ m_vpr_is_valid = false;
+
+ return WriteRegisterSet(&ioVec, GetVPRSize(), NT_RISCV_VECTOR);
+}
+
void NativeRegisterContextLinux_riscv64::InvalidateAllRegisters() {
m_gpr_is_valid = false;
m_fpu_is_valid = false;
+ m_vpr_is_valid = false;
}
uint32_t NativeRegisterContextLinux_riscv64::CalculateFprOffset(
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.h
index d5cc50131cdc3..8e2e6eec9c2c4 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.h
@@ -59,28 +59,41 @@ class NativeRegisterContextLinux_riscv64 : public NativeRegisterContextLinux {
Status WriteFPR() override;
+ Status ReadVPR();
+
+ Status WriteVPR();
+
void *GetGPRBuffer() override { return &m_gpr; }
void *GetFPRBuffer() override { return &m_fpr; }
+ void *GetVPRBuffer() { return m_vpr.GetVPR(); }
+
size_t GetGPRSize() const override { return GetRegisterInfo().GetGPRSize(); }
size_t GetFPRSize() override { return GetRegisterInfo().GetFPRSize(); }
+ size_t GetVPRSize() { return m_vpr.GetSize(); }
+
private:
bool m_gpr_is_valid;
bool m_fpu_is_valid;
+ bool m_vpr_is_valid;
RegisterInfoPOSIX_riscv64::GPR m_gpr;
RegisterInfoPOSIX_riscv64::FPR m_fpr;
+ RegisterInfoPOSIX_riscv64::VPR m_vpr;
+
size_t GetRegContextSize();
bool IsGPR(unsigned reg) const;
bool IsFPR(unsigned reg) const;
+ bool IsVPR(unsigned reg) const;
+
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;
const RegisterInfoPOSIX_riscv64 &GetRegisterInfo() const;
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp
index bbcfb9eae1003..4297f45fa0f6c 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp
@@ -79,3 +79,7 @@ bool RegisterContextPOSIX_riscv64::IsGPR(unsigned int reg) {
bool RegisterContextPOSIX_riscv64::IsFPR(unsigned int reg) {
return m_register_info_up->IsFPReg(reg);
}
+
+bool RegisterContextPOSIX_riscv64::IsVPR(unsigned int reg) {
+ return m_register_info_up->IsVPReg(reg);
+}
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h
index 2431ed6ab8c6d..8bacd3f994a60 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h
@@ -50,6 +50,8 @@ class RegisterContextPOSIX_riscv64 : public lldb_private::RegisterContext {
bool IsFPR(unsigned reg);
+ bool IsVPR(unsigned reg);
+
size_t GetFPRSize() { return sizeof(RegisterInfoPOSIX_riscv64::FPR); }
uint32_t GetRegNumFCSR() const { return fpr_fcsr_riscv; }
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp
index a711e682c9f17..58413c25fc98c 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp
@@ -26,6 +26,9 @@
enum {
k_num_gpr_registers = gpr_last_riscv - gpr_first_riscv + 1,
k_num_fpr_registers = fpr_last_riscv - fpr_first_riscv + 1,
+ k_num_vcsr_registers = vcsr_last_riscv - vcsr_first_riscv + 1,
+ k_num_vpr_registers = vpr_last_riscv - vpr_first_riscv + 1,
+ k_num_vector_registers = k_num_vcsr_registers + k_num_vpr_registers,
k_num_register_sets_default = 1
};
@@ -52,9 +55,12 @@ static const lldb_private::RegisterSet g_reg_set_gpr_riscv64 = {
g_gpr_regnums_riscv64};
static const lldb_private::RegisterSet g_reg_set_fpr_riscv64 = {
"Floating Point Registers", "fpr", k_num_fpr_registers, nullptr};
+static const lldb_private::RegisterSet g_reg_set_vpr_riscv64 = {
+ "Vector Purpose Registers", "vpr", k_num_vector_registers, nullptr};
RegisterInfoPOSIX_riscv64::RegisterInfoPOSIX_riscv64(
- const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
+ const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets,
+ uint64_t vlenb)
: lldb_private::RegisterInfoAndSetInterface(target_arch),
m_opt_regsets(opt_regsets) {
switch (target_arch.GetMachine()) {
@@ -66,6 +72,11 @@ RegisterInfoPOSIX_riscv64::RegisterInfoPOSIX_riscv64(
if (m_opt_regsets.AnySet(eRegsetMaskFP))
AddRegSetFP();
+ if (vlenb > 0) {
+ m_opt_regsets.Set(eRegsetMaskVP);
+ AddRegSetVPR(vlenb);
+ }
+
break;
}
default:
@@ -107,6 +118,37 @@ void RegisterInfoPOSIX_riscv64::AddRegSetFP() {
std::make_pair(register_info_count, m_register_infos.size());
}
+void RegisterInfoPOSIX_riscv64::AddRegSetVPR(uint64_t vlenb) {
+ assert(vlenb && "Target doesn't support V extension");
+
+ const uint32_t register_info_count = m_register_infos.size();
+ const uint32_t register_set_count = m_register_sets.size();
+
+ m_register_infos.resize(register_info_count + k_num_vector_registers);
+ memcpy(&m_register_infos[register_info_count], g_register_infos_riscv64_vpr,
+ sizeof(g_register_infos_riscv64_vpr));
+
+ for (uint32_t i = 0; i < k_num_vcsr_registers; i++)
+ m_vp_regnum_collection.push_back(register_info_count + i);
+
+ // Now we know appropriate vlenb, so update byte offsets and sizes for vector
+ // registers here
+ constexpr size_t vcsr_size = sizeof(uint64_t);
+ for (uint32_t i = 0; i < k_num_vpr_registers; i++) {
+ uint32_t vpr_info_count = register_info_count + k_num_vcsr_registers + i;
+ m_register_infos[vpr_info_count].byte_size = vlenb;
+ m_register_infos[vpr_info_count].byte_offset =
+ (k_num_vcsr_registers * vcsr_size) + (i * vlenb);
+ m_vp_regnum_collection.push_back(vpr_info_count);
+ }
+
+ m_register_sets.push_back(g_reg_set_vpr_riscv64);
+ m_register_sets.back().registers = m_vp_regnum_collection.data();
+
+ m_per_regset_regnum_range[register_set_count] =
+ std::make_pair(register_info_count, m_register_infos.size());
+}
+
uint32_t RegisterInfoPOSIX_riscv64::GetRegisterCount() const {
return m_register_infos.size();
}
@@ -142,6 +184,10 @@ bool RegisterInfoPOSIX_riscv64::IsFPReg(unsigned reg) const {
return llvm::is_contained(m_fp_regnum_collection, reg);
}
+bool RegisterInfoPOSIX_riscv64::IsVPReg(unsigned reg) const {
+ return llvm::is_contained(m_vp_regnum_collection, reg);
+}
+
const lldb_private::RegisterSet *
RegisterInfoPOSIX_riscv64::GetRegisterSet(size_t set_index) const {
if (set_index < GetRegisterSetCount())
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h
index f8e22c7df3c88..f55c48ecdf27b 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h
@@ -24,6 +24,7 @@ class RegisterInfoPOSIX_riscv64
enum {
eRegsetMaskDefault = 0,
eRegsetMaskFP = 1,
+ eRegsetMaskVP = 2,
eRegsetMaskAll = -1,
};
@@ -37,18 +38,42 @@ class RegisterInfoPOSIX_riscv64
uint32_t fcsr;
};
- struct VPR {
- // The size should be VLEN*32 in bits, but we don't have VLEN here.
- void *vpr;
+ class VPR {
+ public:
+ // __riscv_v_regset_state from Linux ptrace API
+ struct RawVPR {
+ uint64_t vstart;
+ uint64_t vl;
+ uint64_t vtype;
+ uint64_t vcsr;
+ uint64_t vlenb;
+ uint8_t v_regs[];
+ };
+
+ VPR() = default;
+
+ VPR(uint64_t vlenb) : m_vpr(sizeof(RawVPR) + 32 * vlenb) {
+ assert(vlenb && "Target doesn't support V extension!");
+ }
+
+ void *GetVPR() { return static_cast<void *>(m_vpr.data()); }
+
+ size_t GetSize() const { return m_vpr.size(); }
+
+ private:
+ std::vector<uint8_t> m_vpr;
};
RegisterInfoPOSIX_riscv64(const lldb_private::ArchSpec &target_arch,
- lldb_private::Flags opt_regsets);
+ lldb_private::Flags opt_regsets,
+ uint64_t vlenb = 0);
void AddRegSetGP();
void AddRegSetFP();
+ void AddRegSetVPR(uint64_t vlenb);
+
size_t GetGPRSize() const override;
size_t GetFPRSize() const override;
@@ -66,8 +91,12 @@ class RegisterInfoPOSIX_riscv64
bool IsFPPresent() const { return m_opt_regsets.AnySet(eRegsetMaskFP); }
+ bool IsVPPresent() const { return m_opt_regsets.AnySet(eRegsetMaskVP); }
+
bool IsFPReg(unsigned reg) const;
+ bool IsVPReg(unsigned reg) const;
+
private:
std::vector<lldb_private::RegisterInfo> m_register_infos;
@@ -79,6 +108,7 @@ class RegisterInfoPOSIX_riscv64
// Register collections to be stored as reference for m_register_sets items
std::vector<uint32_t> m_fp_regnum_collection;
+ std::vector<uint32_t> m_vp_regnum_collection;
lldb_private::Flags m_opt_regsets;
};
More information about the llvm-branch-commits
mailing list