[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