omjavaid updated this revision to Diff 263935.
omjavaid added a comment.
This new updated patch overrides GetRegisterInfoAtIndex in
NativeRegisterContextLinux_arm64 to reduce the impacts of register numbering
conversion on the generic LLGS code.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D79699/new/
https://reviews.llvm.org/D79699
Files:
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
lldb/test/API/commands/register/register/aarch64_sve_registers/Makefile
lldb/test/API/commands/register/register/aarch64_sve_registers/TestSVERegisters.py
lldb/test/API/commands/register/register/aarch64_sve_registers/main.c
Index: lldb/test/API/commands/register/register/aarch64_sve_registers/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/commands/register/register/aarch64_sve_registers/main.c
@@ -0,0 +1,5 @@
+int main() {
+ asm volatile("ptrue p0.s\n\t");
+ asm volatile("fcpy z0.s, p0/m, #5.00000000\n\t");
+ return 0; // Set a break point here.
+}
\ No newline at end of file
Index: lldb/test/API/commands/register/register/aarch64_sve_registers/TestSVERegisters.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/register/register/aarch64_sve_registers/TestSVERegisters.py
@@ -0,0 +1,128 @@
+"""
+Test the AArch64 SVE registers.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class RegisterCommandsTestCase(TestBase):
+
+ def check_sve_register_size(self, set, name, expected):
+ reg_value = set.GetChildMemberWithName(name)
+ self.assertTrue(reg_value.IsValid(),
+ 'Verify we have a register named "%s"' % (name))
+ self.assertEqual(reg_value.GetByteSize(), expected,
+ 'Verify "%s" == %i' % (name, expected))
+
+ mydir = TestBase.compute_mydir(__file__)
+ @skipIf
+ def test_sve_registers_configuration(self):
+ """Test AArch64 SVE registers size configuration."""
+ self.build()
+ self.line = line_number('main.c', '// Set a break point here.')
+
+ exe = self.getBuildArtifact("a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+ lldbutil.run_break_set_by_file_and_line(self, "main.c", self.line, num_expected_locations=1)
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
+ substrs = ["stop reason = breakpoint 1."])
+
+ target = self.dbg.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetThreadAtIndex(0)
+ currentFrame = thread.GetFrameAtIndex(0)
+
+ has_sve = False
+ for registerSet in currentFrame.GetRegisters():
+ if 'sve registers' in registerSet.GetName().lower():
+ has_sve = True
+
+ if not has_sve:
+ self.skipTest('SVE registers must be supported.')
+
+ registerSets = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters()
+
+ sve_registers = registerSets.GetValueAtIndex(2)
+
+ vg_reg = sve_registers.GetChildMemberWithName("vg")
+
+ vg_reg_value = sve_registers.GetChildMemberWithName("vg").GetValueAsUnsigned()
+
+ z_reg_size = vg_reg_value * 8
+
+ p_reg_size = z_reg_size / 8
+
+ for i in range(32):
+ self.check_sve_register_size(sve_registers, 'z%i' % (i), z_reg_size)
+
+ for i in range(16):
+ self.check_sve_register_size(sve_registers, 'p%i' % (i), p_reg_size)
+
+ self.check_sve_register_size(sve_registers, 'ffr', p_reg_size)
+
+ mydir = TestBase.compute_mydir(__file__)
+ @no_debug_info_test
+ @skipIf
+ def test_sve_registers_read_write(self):
+ """Test AArch64 SVE registers read and write."""
+ self.build()
+ self.line = line_number('main.c', '// Set a break point here.')
+
+ exe = self.getBuildArtifact("a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+ lldbutil.run_break_set_by_file_and_line(self, "main.c", self.line, num_expected_locations=1)
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
+ substrs = ["stop reason = breakpoint 1."])
+
+ target = self.dbg.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetThreadAtIndex(0)
+ currentFrame = thread.GetFrameAtIndex(0)
+
+ has_sve = False
+ for registerSet in currentFrame.GetRegisters():
+ if 'sve registers' in registerSet.GetName().lower():
+ has_sve = True
+
+ if not has_sve:
+ self.skipTest('SVE registers must be supported.')
+
+ registerSets = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters()
+
+ sve_registers = registerSets.GetValueAtIndex(2)
+
+ vg_reg = sve_registers.GetChildMemberWithName("vg")
+
+ vg_reg_value = sve_registers.GetChildMemberWithName("vg").GetValueAsUnsigned()
+
+ z_reg_size = vg_reg_value * 8
+
+ p_reg_size = z_reg_size / 8
+
+ z_regs_value = '{' + ' '.join(('0x9d' for _ in range(z_reg_size))) + '}'
+
+ p_regs_value = '{' + ' '.join(('0xee' for _ in range(p_reg_size))) + '}'
+
+ for i in range(32):
+ self.runCmd('register write ' + 'z%i' % (i) + " '" + z_regs_value + "'")
+
+ for i in range(32):
+ self.expect("register read " + 'z%i' % (i), substrs = [z_regs_value])
+
+ for i in range(16):
+ self.runCmd('register write ' + 'p%i' % (i) + " '" + p_regs_value + "'")
+
+ for i in range(16):
+ self.expect("register read " + 'p%i' % (i), substrs = [p_regs_value])
+
+ self.runCmd('register write ' + 'ffr ' + "'" + p_regs_value + "'")
+
+ self.expect("register read " + 'ffr', substrs = [p_regs_value])
\ No newline at end of file
Index: lldb/test/API/commands/register/register/aarch64_sve_registers/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/commands/register/register/aarch64_sve_registers/Makefile
@@ -0,0 +1,5 @@
+C_SOURCES := main.c
+
+CFLAGS_EXTRAS := -march=armv8-a+sve
+
+include Makefile.rules
Index: lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
@@ -39,6 +39,9 @@
std::string &description) override;
NativeRegisterContextLinux &GetRegisterContext() override {
+ if (m_reg_context_up && IsStopped(nullptr))
+ m_reg_context_up->ConfigureRegisterContext();
+
return *m_reg_context_up;
}
Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -14,11 +14,25 @@
#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
+#include <asm/ptrace.h>
+
+#ifndef SVE_PT_REGS_SVE
+#define INCLUDE_LINUX_PTRACE_DEFINITIONS_FOR_SVE_ARM64
+#include "Plugins/Process/Linux/LinuxPTraceDefines_arm64sve.h"
+#endif
+
namespace lldb_private {
namespace process_linux {
class NativeProcessLinux;
+enum class SVE_STATE {
+ SVE_STATE_UNKNOWN,
+ SVE_STATE_DISABLED,
+ SVE_STATE_FPSIMD,
+ SVE_STATE_FULL
+};
+
class NativeRegisterContextLinux_arm64 : public NativeRegisterContextLinux {
public:
NativeRegisterContextLinux_arm64(const ArchSpec &target_arch,
@@ -28,6 +42,12 @@
uint32_t GetUserRegisterCount() const override;
+ uint32_t UserRegIndexToRegInfosIndex(uint32_t reg_infos_index) const;
+
+ const RegisterInfo *GetRegisterInfoAtIndex(uint32_t reg_index) const override;
+
+ uint32_t RegInfosIndexToUserRegIndex(uint32_t user_reg_index) const override;
+
const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
Status ReadRegister(const RegisterInfo *reg_info,
@@ -42,6 +62,8 @@
void InvalidateAllRegisters() override;
+ void ConfigureRegisterContext() override;
+
// Hardware breakpoints/watchpoint management functions
uint32_t NumSupportedHardwareBreakpoints() override;
@@ -88,17 +110,34 @@
Status WriteFPR() override;
+ Status ReadAllSVE();
+
+ Status WriteAllSVE();
+
+ Status ReadSVEHeader();
+
+ Status WriteSVEHeader();
+
void *GetGPRBuffer() override { return &m_gpr_arm64; }
void *GetFPRBuffer() override { return &m_fpr; }
size_t GetFPRSize() override { return sizeof(m_fpr); }
+ void *GetSVEHeader() { return &m_sve_header; }
+
+ void *GetSVEBuffer();
+
+ size_t GetSVEHeaderSize() { return sizeof(m_sve_header); }
+
+ size_t GetSVEBufferSize() { return m_sve_ptrace_payload.size(); }
+
private:
struct RegInfo {
uint32_t num_registers;
uint32_t num_gpr_registers;
uint32_t num_fpr_registers;
+ uint32_t num_sve_registers;
uint32_t last_gpr;
uint32_t first_fpr;
@@ -107,6 +146,9 @@
uint32_t first_fpr_v;
uint32_t last_fpr_v;
+ uint32_t first_sve;
+ uint32_t last_sve;
+
uint32_t gpr_flags;
};
@@ -131,11 +173,20 @@
bool m_gpr_is_valid;
bool m_fpu_is_valid;
+ bool m_sve_buffer_is_valid;
+
+ bool m_sve_header_is_valid;
+ bool m_sve_update_reg_infos;
GPR m_gpr_arm64; // 64-bit general purpose registers.
+
RegInfo m_reg_info;
FPU m_fpr; // floating-point registers including extended register sets.
+ mutable SVE_STATE m_sve_state;
+ struct user_sve_header m_sve_header;
+ std::vector<uint8_t> m_sve_ptrace_payload;
+
// Debug register info for hardware breakpoints and watchpoints management.
struct DREG {
lldb::addr_t address; // Breakpoint/watchpoint address value.
@@ -157,11 +208,25 @@
bool IsFPR(unsigned reg) const;
+ bool IsSVE(unsigned reg) const;
+
+ bool IsSVEZReg(unsigned reg) const;
+
+ bool IsSVEPReg(unsigned reg) const;
+
+ bool IsSVERegVG(unsigned reg) const;
+
+ uint64_t GetSVERegVG() { return m_sve_header.vl / 8; }
+
+ void SetSVERegVG(uint64_t vg) { m_sve_header.vl = vg * 8; }
+
Status ReadHardwareDebugInfo();
Status WriteHardwareDebugRegs(int hwbType);
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;
+
+ uint32_t CalculateSVEOffset(uint32_t reg_num) const;
};
} // namespace process_linux
Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -28,8 +28,10 @@
#include <sys/socket.h>
// NT_PRSTATUS and NT_FPREGSET definition
#include <elf.h>
-// user_hwdebug_state definition
-#include <asm/ptrace.h>
+
+#ifndef NT_ARM_SVE
+#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension */
+#endif
#define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize())
@@ -95,9 +97,34 @@
1) == k_num_fpr_registers_arm64,
"g_fpu_regnums_arm64 has wrong number of register infos");
+// ARM64 SVE registers.
+static const uint32_t g_sve_regnums_arm64[] = {
+ sve_vg_arm64,
+
+ sve_z0_arm64, sve_z1_arm64, sve_z2_arm64, sve_z3_arm64,
+ sve_z4_arm64, sve_z5_arm64, sve_z6_arm64, sve_z7_arm64,
+ sve_z8_arm64, sve_z9_arm64, sve_z10_arm64, sve_z11_arm64,
+ sve_z12_arm64, sve_z13_arm64, sve_z14_arm64, sve_z15_arm64,
+ sve_z16_arm64, sve_z17_arm64, sve_z18_arm64, sve_z19_arm64,
+ sve_z20_arm64, sve_z21_arm64, sve_z22_arm64, sve_z23_arm64,
+ sve_z24_arm64, sve_z25_arm64, sve_z26_arm64, sve_z27_arm64,
+ sve_z28_arm64, sve_z29_arm64, sve_z30_arm64, sve_z31_arm64,
+
+ sve_p0_arm64, sve_p1_arm64, sve_p2_arm64, sve_p3_arm64,
+ sve_p4_arm64, sve_p5_arm64, sve_p6_arm64, sve_p7_arm64,
+ sve_p8_arm64, sve_p9_arm64, sve_p10_arm64, sve_p11_arm64,
+ sve_p12_arm64, sve_p13_arm64, sve_p14_arm64, sve_p15_arm64,
+
+ sve_ffr_arm64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_sve_regnums_arm64 / sizeof g_sve_regnums_arm64[0]) -
+ 1) == k_num_sve_registers_arm64,
+ "g_sve_regnums_arm64 has wrong number of register infos");
+
namespace {
// Number of register sets provided by this context.
-enum { k_num_register_sets = 2 };
+enum { k_num_register_sets = 3 };
}
// Register sets for ARM64.
@@ -105,7 +132,8 @@
{"General Purpose Registers", "gpr", k_num_gpr_registers_arm64,
g_gpr_regnums_arm64},
{"Floating Point Registers", "fpu", k_num_fpr_registers_arm64,
- g_fpu_regnums_arm64}};
+ g_fpu_regnums_arm64},
+ {"SVE Registers", "sve", k_num_sve_registers_arm64, g_sve_regnums_arm64}};
std::unique_ptr<NativeRegisterContextLinux>
NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
@@ -131,12 +159,16 @@
m_reg_info.num_registers = k_num_registers_arm64;
m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64;
m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64;
+ m_reg_info.num_sve_registers = k_num_sve_registers_arm64;
+
m_reg_info.last_gpr = k_last_gpr_arm64;
m_reg_info.first_fpr = k_first_fpr_arm64;
m_reg_info.last_fpr = k_last_fpr_arm64;
m_reg_info.first_fpr_v = fpu_v0_arm64;
m_reg_info.last_fpr_v = fpu_v31_arm64;
m_reg_info.gpr_flags = gpr_cpsr_arm64;
+ m_reg_info.first_sve = sve_vg_arm64;
+ m_reg_info.last_sve = sve_ffr_arm64;
break;
default:
llvm_unreachable("Unhandled target architecture.");
@@ -147,6 +179,7 @@
::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64));
::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs));
+ ::memset(&m_sve_header, 0, sizeof(m_sve_header));
// 16 is just a maximum value, query hardware for actual watchpoint count
m_max_hwp_supported = 16;
@@ -155,25 +188,68 @@
m_gpr_is_valid = false;
m_fpu_is_valid = false;
+ m_sve_buffer_is_valid = false;
+
+ m_sve_header_is_valid = false;
+ m_sve_update_reg_infos = true;
+
+ // SVE is not enabled until we query user_sve_header
+ m_sve_state = SVE_STATE::SVE_STATE_UNKNOWN;
}
uint32_t NativeRegisterContextLinux_arm64::GetRegisterSetCount() const {
- return k_num_register_sets;
+ if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD ||
+ m_sve_state == SVE_STATE::SVE_STATE_FULL)
+
+ return k_num_register_sets;
+ else
+ return k_num_register_sets - 1;
}
const RegisterSet *
NativeRegisterContextLinux_arm64::GetRegisterSet(uint32_t set_index) const {
- if (set_index < k_num_register_sets)
+ if (set_index < GetRegisterSetCount())
return &g_reg_sets_arm64[set_index];
return nullptr;
}
uint32_t NativeRegisterContextLinux_arm64::GetUserRegisterCount() const {
- uint32_t count = 0;
- for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index)
- count += g_reg_sets_arm64[set_index].num_registers;
- return count;
+ if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD ||
+ m_sve_state == SVE_STATE::SVE_STATE_FULL)
+ return k_num_gpr_registers_arm64 + k_num_fpr_registers_arm64 +
+ k_num_sve_registers_arm64;
+
+ return k_num_gpr_registers_arm64 + k_num_fpr_registers_arm64;
+}
+
+uint32_t NativeRegisterContextLinux_arm64::RegInfosIndexToUserRegIndex(
+ uint32_t reg_infos_index) const {
+ if (reg_infos_index > k_num_registers_arm64)
+ return LLDB_INVALID_REGNUM;
+
+ if (reg_infos_index >= k_num_gpr_registers_arm64 + k_num_fpr_registers_arm64)
+ return reg_infos_index - (m_reg_info.first_sve - m_reg_info.last_fpr + 1);
+ else
+ return reg_infos_index;
+}
+
+uint32_t NativeRegisterContextLinux_arm64::UserRegIndexToRegInfosIndex(
+ uint32_t user_reg_index) const {
+ if (user_reg_index > k_num_gpr_registers_arm64 + k_num_fpr_registers_arm64 +
+ k_num_sve_registers_arm64)
+ return LLDB_INVALID_REGNUM;
+
+ if (user_reg_index >= k_num_gpr_registers_arm64 + k_num_fpr_registers_arm64)
+ return user_reg_index + (m_reg_info.first_sve - m_reg_info.last_fpr - 1);
+ else
+ return user_reg_index;
+}
+
+const RegisterInfo *NativeRegisterContextLinux_arm64::GetRegisterInfoAtIndex(
+ uint32_t reg_index) const {
+ return NativeRegisterContextRegisterInfo::GetRegisterInfoAtIndex(
+ UserRegIndexToRegInfosIndex(reg_index));
}
Status
@@ -195,6 +271,8 @@
uint8_t *src;
uint32_t offset;
+ uint64_t sve_vg;
+ std::vector<uint8_t> sve_reg_non_live;
if (IsGPR(reg)) {
if (!m_gpr_is_valid) {
@@ -208,15 +286,70 @@
src = (uint8_t *)GetGPRBuffer() + offset;
} else if (IsFPR(reg)) {
- if (!m_fpu_is_valid) {
+ if (m_sve_state == SVE_STATE::SVE_STATE_DISABLED) {
+ // SVE is disabled take legacy route for FPU register access
+ if (!m_fpu_is_valid) {
- error = ReadFPR();
- if (error.Fail())
- return error;
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+ }
+ offset = CalculateFprOffset(reg_info);
+ assert(offset < GetFPRSize());
+ src = (uint8_t *)GetFPRBuffer() + offset;
+ } else {
+ // SVE enabled, we will read and cache SVE ptrace data
+ if (!m_sve_buffer_is_valid) {
+ error = ReadAllSVE();
+ if (error.Fail())
+ return error;
+ }
+ // Extract SVE Z register value register number for this reg_info
+ uint32_t sve_reg_num = LLDB_INVALID_REGNUM;
+ if (reg_info->value_regs &&
+ reg_info->value_regs[0] != LLDB_INVALID_REGNUM)
+ sve_reg_num = reg_info->value_regs[0];
+ else if (reg == fpu_fpcr_arm64 || reg == fpu_fpsr_arm64)
+ sve_reg_num = reg;
+ if (sve_reg_num != LLDB_INVALID_REGNUM) {
+ offset = CalculateSVEOffset(sve_reg_num);
+ assert(offset < GetSVEBufferSize());
+ src = (uint8_t *)GetSVEBuffer() + offset;
+ }
+ }
+ } else if (IsSVERegVG(reg)) {
+
+ sve_vg = GetSVERegVG();
+ src = (uint8_t *)&sve_vg;
+
+ } else if (IsSVE(reg)) {
+ if (m_sve_state == SVE_STATE::SVE_STATE_DISABLED) {
+ return Status("SVE disabled or not supported");
+ } else {
+ // SVE enabled, we will read and cache SVE ptrace data
+ if (!m_sve_buffer_is_valid) {
+ error = ReadAllSVE();
+ if (error.Fail())
+ return error;
+ }
+ if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD) {
+ sve_reg_non_live.resize(reg_info->byte_size, 0);
+ // In FPSIMD state SVE payload mirrors legacy fpsimd struct and so just
+ // copy 16 bytes of v register to the start of z register. All other
+ // SVE register will be set to zero.
+ if (IsSVEZReg(reg)) {
+ offset = CalculateSVEOffset(reg);
+ assert(offset < GetSVEBufferSize());
+ ::memcpy(sve_reg_non_live.data(), (uint8_t *)GetSVEBuffer() + offset,
+ 16);
+ }
+ src = sve_reg_non_live.data();
+ } else if (m_sve_state == SVE_STATE::SVE_STATE_FULL) {
+ offset = CalculateSVEOffset(reg);
+ assert(offset < GetSVEBufferSize());
+ src = (uint8_t *)GetSVEBuffer() + offset;
+ }
}
- offset = CalculateFprOffset(reg_info);
- assert(offset < GetFPRSize());
- src = (uint8_t *)GetFPRBuffer() + offset;
} else
return Status("failed - register wasn't recognized to be a GPR or an FPR, "
"write strategy unknown");
@@ -243,6 +376,7 @@
uint8_t *dst;
uint32_t offset;
+ std::vector<uint8_t> sve_reg_non_live;
if (IsGPR(reg)) {
if (!m_gpr_is_valid) {
@@ -259,20 +393,96 @@
return WriteGPR();
} else if (IsFPR(reg)) {
- if (!m_fpu_is_valid) {
- error = ReadFPR();
- if (error.Fail())
- return error;
+ if (m_sve_state == SVE_STATE::SVE_STATE_DISABLED) {
+ // SVE is disabled take legacy route for FPU register access
+ if (!m_fpu_is_valid) {
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+ }
+ offset = CalculateFprOffset(reg_info);
+ assert(offset < GetFPRSize());
+ dst = (uint8_t *)GetFPRBuffer() + offset;
+
+ ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
+
+ return WriteFPR();
+ } else {
+ // SVE enabled, we will read and cache SVE ptrace data
+ if (!m_sve_buffer_is_valid) {
+ error = ReadAllSVE();
+ if (error.Fail())
+ return error;
+ }
+ // Extract SVE Z register value register number for this reg_info
+ uint32_t sve_reg_num = LLDB_INVALID_REGNUM;
+ if (reg_info->value_regs &&
+ reg_info->value_regs[0] != LLDB_INVALID_REGNUM)
+ sve_reg_num = reg_info->value_regs[0];
+ else if (reg == fpu_fpcr_arm64 || reg == fpu_fpsr_arm64)
+ sve_reg_num = reg;
+ if (sve_reg_num != LLDB_INVALID_REGNUM) {
+ offset = CalculateSVEOffset(sve_reg_num);
+ assert(offset < GetSVEBufferSize());
+ dst = (uint8_t *)GetSVEBuffer() + offset;
+ ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
+ return WriteAllSVE();
+ }
}
- offset = CalculateFprOffset(reg_info);
- assert(offset < GetFPRSize());
- dst = (uint8_t *)GetFPRBuffer() + offset;
-
- ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
+ } else if (IsSVERegVG(reg)) {
+ return Status("SVE state change operation not supported");
+ } else if (IsSVE(reg)) {
+ if (m_sve_state == SVE_STATE::SVE_STATE_DISABLED) {
+ return Status("SVE disabled or not supported");
+ } else {
+ // Target has SVE enabled, we will read and cache SVE ptrace data
+ if (!m_sve_buffer_is_valid) {
+ error = ReadAllSVE();
+ if (error.Fail())
+ return error;
+ }
- return WriteFPR();
+ // If target supports SVE but currently in FPSIMD mode.
+ if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD) {
+ // Here we will check if writing this SVE register enables
+ // SVE_STATE_FULL
+ bool set_sve_state_full = false;
+ const uint8_t *reg_bytes = (const uint8_t *)reg_value.GetBytes();
+ if (IsSVEZReg(reg)) {
+ for (uint32_t i = 16; i < reg_info->byte_size; i++) {
+ if (reg_bytes[i]) {
+ set_sve_state_full = true;
+ break;
+ }
+ }
+ } else if (IsSVEPReg(reg) || reg == sve_ffr_arm64) {
+ for (uint32_t i = 0; i < reg_info->byte_size; i++) {
+ if (reg_bytes[i]) {
+ set_sve_state_full = true;
+ break;
+ }
+ }
+ }
+ if (set_sve_state_full) {
+ return Status("SVE state change operation not supported");
+ } else if (!set_sve_state_full && IsSVEZReg(reg)) {
+ // We are writing a Z register which is zero beyond 16 bytes so copy
+ // first 16 bytes only as SVE payload mirrors legacy fpsimd structure
+ offset = CalculateSVEOffset(reg);
+ assert(offset < GetSVEBufferSize());
+ dst = (uint8_t *)GetSVEBuffer() + offset;
+ ::memcpy(dst, reg_value.GetBytes(), 16);
+ return WriteAllSVE();
+ }
+ } else if (m_sve_state == SVE_STATE::SVE_STATE_FULL) {
+ offset = CalculateSVEOffset(reg);
+ assert(offset < GetSVEBufferSize());
+ dst = (uint8_t *)GetSVEBuffer() + offset;
+ ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
+ return WriteAllSVE();
+ }
+ }
}
-
return error;
}
@@ -351,6 +561,22 @@
return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
}
+bool NativeRegisterContextLinux_arm64::IsSVE(unsigned reg) const {
+ return (m_reg_info.first_sve <= reg && reg <= m_reg_info.last_sve);
+}
+
+bool NativeRegisterContextLinux_arm64::IsSVEZReg(unsigned reg) const {
+ return (sve_z0_arm64 <= reg && reg <= sve_z31_arm64);
+}
+
+bool NativeRegisterContextLinux_arm64::IsSVEPReg(unsigned reg) const {
+ return (sve_p0_arm64 <= reg && reg <= sve_p15_arm64);
+}
+
+bool NativeRegisterContextLinux_arm64::IsSVERegVG(unsigned reg) const {
+ return (m_reg_info.first_sve == reg);
+}
+
uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
@@ -770,138 +996,255 @@
return m_hwp_regs[wp_index].hit_addr;
else
return LLDB_INVALID_ADDRESS;
-}
-
-Status NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
- if (!m_refresh_hwdebug_info) {
- return Status();
}
- ::pid_t tid = m_thread.GetID();
+ Status NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
+ if (!m_refresh_hwdebug_info) {
+ return Status();
+ }
- int regset = NT_ARM_HW_WATCH;
- struct iovec ioVec;
- struct user_hwdebug_state dreg_state;
- Status error;
+ ::pid_t tid = m_thread.GetID();
- ioVec.iov_base = &dreg_state;
- ioVec.iov_len = sizeof(dreg_state);
- error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set,
- &ioVec, ioVec.iov_len);
+ int regset = NT_ARM_HW_WATCH;
+ struct iovec ioVec;
+ struct user_hwdebug_state dreg_state;
+ Status error;
+
+ ioVec.iov_base = &dreg_state;
+ ioVec.iov_len = sizeof(dreg_state);
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set,
+ &ioVec, ioVec.iov_len);
+
+ if (error.Fail())
+ return error;
+
+ m_max_hwp_supported = dreg_state.dbg_info & 0xff;
+
+ regset = NT_ARM_HW_BREAK;
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set,
+ &ioVec, ioVec.iov_len);
+
+ if (error.Fail())
+ return error;
+
+ m_max_hbp_supported = dreg_state.dbg_info & 0xff;
+ m_refresh_hwdebug_info = false;
- if (error.Fail())
return error;
+ }
- m_max_hwp_supported = dreg_state.dbg_info & 0xff;
+ Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) {
+ struct iovec ioVec;
+ struct user_hwdebug_state dreg_state;
+ Status error;
- regset = NT_ARM_HW_BREAK;
- error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set,
- &ioVec, ioVec.iov_len);
+ memset(&dreg_state, 0, sizeof(dreg_state));
+ ioVec.iov_base = &dreg_state;
+
+ if (hwbType == eDREGTypeWATCH) {
+ hwbType = NT_ARM_HW_WATCH;
+ ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
+ (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
+
+ for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
+ dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
+ dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
+ }
+ } else {
+ hwbType = NT_ARM_HW_BREAK;
+ ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
+ (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
+
+ for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
+ dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address;
+ dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control;
+ }
+ }
+
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
+ &hwbType, &ioVec, ioVec.iov_len);
+ }
+
+ Status NativeRegisterContextLinux_arm64::ReadGPR() {
+ Status error;
+
+ struct iovec ioVec;
+
+ ioVec.iov_base = GetGPRBuffer();
+ ioVec.iov_len = GetGPRSize();
+
+ error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
+
+ if (error.Success())
+ m_gpr_is_valid = true;
- if (error.Fail())
return error;
+ }
- m_max_hbp_supported = dreg_state.dbg_info & 0xff;
- m_refresh_hwdebug_info = false;
+ Status NativeRegisterContextLinux_arm64::WriteGPR() {
+ struct iovec ioVec;
- return error;
-}
+ m_gpr_is_valid = false;
-Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) {
- struct iovec ioVec;
- struct user_hwdebug_state dreg_state;
- Status error;
+ ioVec.iov_base = GetGPRBuffer();
+ ioVec.iov_len = GetGPRSize();
- memset(&dreg_state, 0, sizeof(dreg_state));
- ioVec.iov_base = &dreg_state;
+ return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
+ }
- if (hwbType == eDREGTypeWATCH) {
- hwbType = NT_ARM_HW_WATCH;
- ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
- (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
+ Status NativeRegisterContextLinux_arm64::ReadFPR() {
+ Status error;
- for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
- dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
- dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
- }
- } else {
- hwbType = NT_ARM_HW_BREAK;
- ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
- (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
-
- for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
- dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address;
- dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control;
- }
+ struct iovec ioVec;
+
+ ioVec.iov_base = GetFPRBuffer();
+ ioVec.iov_len = GetFPRSize();
+
+ error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
+
+ if (error.Success())
+ m_fpu_is_valid = true;
+
+ return error;
}
- return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
- &hwbType, &ioVec, ioVec.iov_len);
-}
+ Status NativeRegisterContextLinux_arm64::WriteFPR() {
+ struct iovec ioVec;
-Status NativeRegisterContextLinux_arm64::ReadGPR() {
- Status error;
+ m_fpu_is_valid = false;
+
+ ioVec.iov_base = GetFPRBuffer();
+ ioVec.iov_len = GetFPRSize();
- struct iovec ioVec;
+ return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
+ }
- ioVec.iov_base = GetGPRBuffer();
- ioVec.iov_len = GetGPRSize();
+ Status NativeRegisterContextLinux_arm64::ReadSVEHeader() {
+ Status error;
- error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
+ struct iovec ioVec;
- if (error.Success())
- m_gpr_is_valid = true;
+ ioVec.iov_base = GetSVEHeader();
+ ioVec.iov_len = GetSVEHeaderSize();
- return error;
-}
+ error = ReadRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE);
-Status NativeRegisterContextLinux_arm64::WriteGPR() {
- struct iovec ioVec;
+ m_sve_header_is_valid = true;
- m_gpr_is_valid = false;
+ return error;
+ }
- ioVec.iov_base = GetGPRBuffer();
- ioVec.iov_len = GetGPRSize();
+ Status NativeRegisterContextLinux_arm64::WriteSVEHeader() {
+ Status error;
- return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
-}
+ struct iovec ioVec;
-Status NativeRegisterContextLinux_arm64::ReadFPR() {
- Status error;
+ ioVec.iov_base = GetSVEHeader();
+ ioVec.iov_len = GetSVEHeaderSize();
- struct iovec ioVec;
+ m_sve_buffer_is_valid = false;
+ m_sve_header_is_valid = false;
- ioVec.iov_base = GetFPRBuffer();
- ioVec.iov_len = GetFPRSize();
+ return WriteRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE);
+ }
- error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
+ Status NativeRegisterContextLinux_arm64::ReadAllSVE() {
+ Status error;
- if (error.Success())
- m_fpu_is_valid = true;
+ struct iovec ioVec;
- return error;
-}
+ ioVec.iov_base = GetSVEBuffer();
+ ioVec.iov_len = GetSVEBufferSize();
-Status NativeRegisterContextLinux_arm64::WriteFPR() {
- struct iovec ioVec;
+ error = ReadRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE);
- m_fpu_is_valid = false;
+ if (error.Success())
+ m_sve_buffer_is_valid = true;
- ioVec.iov_base = GetFPRBuffer();
- ioVec.iov_len = GetFPRSize();
+ return error;
+ }
- return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
-}
+ Status NativeRegisterContextLinux_arm64::WriteAllSVE() {
+ Status error;
-void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
- m_gpr_is_valid = false;
- m_fpu_is_valid = false;
-}
+ struct iovec ioVec;
-uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset(
- const RegisterInfo *reg_info) const {
- return reg_info->byte_offset -
- GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset;
-}
+ ioVec.iov_base = GetSVEBuffer();
+ ioVec.iov_len = GetSVEBufferSize();
+
+ m_sve_buffer_is_valid = false;
+ m_sve_header_is_valid = false;
+
+ return WriteRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE);
+ }
+
+ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
+ m_gpr_is_valid = false;
+ m_fpu_is_valid = false;
+ m_sve_buffer_is_valid = false;
+ m_sve_header_is_valid = false;
+ }
+
+ void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
+ if (!m_sve_header_is_valid) {
+ Status error = ReadSVEHeader();
+
+ if (error.Success()) {
+ if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD)
+ m_sve_state = SVE_STATE::SVE_STATE_FPSIMD;
+ else if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE)
+ m_sve_state = SVE_STATE::SVE_STATE_FULL;
+
+ if (sve_vl_valid(m_sve_header.vl)) {
+ size_t vq = sve_vq_from_vl(m_sve_header.vl);
+ SetRegisterInfoMode(vq);
+ m_sve_ptrace_payload.resize(SVE_PT_SIZE(vq, SVE_PT_REGS_SVE));
+ } else {
+ m_sve_state = SVE_STATE::SVE_STATE_DISABLED;
+ SetRegisterInfoMode(eRegisterInfoModeAArch64);
+ }
+ } else {
+ m_sve_state = SVE_STATE::SVE_STATE_DISABLED;
+ SetRegisterInfoMode(eRegisterInfoModeAArch64);
+ }
+ }
+ }
+
+ uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset(
+ const RegisterInfo *reg_info) const {
+ return reg_info->byte_offset -
+ GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset;
+ }
+
+ uint32_t
+ NativeRegisterContextLinux_arm64::CalculateSVEOffset(uint32_t reg_num) const {
+ if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD) {
+ if (IsSVEZReg(reg_num))
+ return (reg_num - sve_z0_arm64) * 16;
+ else if (reg_num == fpu_fpsr_arm64)
+ return 32 * 16;
+ else if (reg_num == fpu_fpcr_arm64)
+ return (32 * 16) + 4;
+ } else if (m_sve_state == SVE_STATE::SVE_STATE_FULL) {
+ size_t vq = sve_vq_from_vl(m_sve_header.vl);
+ if (IsSVEZReg(reg_num))
+ return SVE_PT_SVE_ZREG_OFFSET(vq, reg_num - sve_z0_arm64);
+ else if (IsSVEPReg(reg_num))
+ return SVE_PT_SVE_PREG_OFFSET(vq, reg_num - sve_p0_arm64);
+ else if (reg_num == sve_ffr_arm64)
+ return SVE_PT_SVE_FFR_OFFSET(vq);
+ else if (reg_num == fpu_fpsr_arm64)
+ return SVE_PT_SVE_FPSR_OFFSET(vq);
+ else if (reg_num == fpu_fpcr_arm64)
+ return SVE_PT_SVE_FPCR_OFFSET(vq);
+ }
+ return 0;
+ }
+
+ void *NativeRegisterContextLinux_arm64::GetSVEBuffer() {
+ if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD)
+ return m_sve_ptrace_payload.data() + SVE_PT_FPSIMD_OFFSET;
+ return m_sve_ptrace_payload.data();
+ }
#endif // defined (__arm64__) || defined (__aarch64__)
Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
@@ -31,6 +31,9 @@
// Invalidates cached values in register context data structures
virtual void InvalidateAllRegisters(){}
+ // Configures register context based on target capabilities
+ virtual void ConfigureRegisterContext() {}
+
protected:
lldb::ByteOrder GetByteOrder() const;
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits