[Lldb-commits] [lldb] [lldb][AArch64][Linux] Track all register cache validity in one place (PR #197113)
David Spickett via lldb-commits
lldb-commits at lists.llvm.org
Tue May 12 01:09:11 PDT 2026
https://github.com/DavidSpickett updated https://github.com/llvm/llvm-project/pull/197113
>From 9de43dc4db8fee735ecaaf99d9f51480a5a1638f Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at arm.com>
Date: Thu, 2 Apr 2026 13:19:01 +0000
Subject: [PATCH 1/2] [lldb][AArch64][Linux] Track all register cache validity
in one place
In this change I've expanded the existing RegisterSetType enum
to be used as part of a singular cache tracking object which replaces
all the separate booleans. These booleans were hard to track and
set/reset in the right places.
This new validity object can be reset by default constructing,
and uses a member initialiser itself so everything starts out
as invalid.
RegisterSetType is now a bitmask enum for easier storage.
It's 32-bit now which is enough for now. Just in case we expand it,
I have updated all handling of it to use sizeof(RegisterSetType)
where we used to hardcode uint32_t.
Over time, RegisterSetType will become the key for all the
operations in the register context. You can see a preview of
that "vision" in #193174.
---
.../NativeRegisterContextLinux_arm64.cpp | 246 ++++++++----------
.../Linux/NativeRegisterContextLinux_arm64.h | 75 ++++--
2 files changed, 169 insertions(+), 152 deletions(-)
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
index c7bd5d7bec252..1beb99d0571bd 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -235,18 +235,6 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
m_refresh_hwdebug_info = true;
- m_gpr_is_valid = false;
- m_fpu_is_valid = false;
- m_sve_buffer_is_valid = false;
- m_sve_header_is_valid = false;
- m_pac_mask_is_valid = false;
- m_mte_ctrl_is_valid = false;
- m_tls_is_valid = false;
- m_zt_buffer_is_valid = false;
- m_fpmr_is_valid = false;
- m_gcs_is_valid = false;
- m_poe_is_valid = false;
-
// SME adds the tpidr2 register
m_tls_size = GetRegisterInfo().IsSSVEPresent() ? sizeof(m_tls_regs)
: sizeof(m_tls_regs.tpidr_reg);
@@ -678,7 +666,8 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
uint64_t vg_value = reg_value.GetAsUInt64();
if (sve::vl_valid(vg_value * 8)) {
- if (m_sve_header_is_valid && vg_value == GetSVERegVG())
+ if (m_validity.IsValid(RegisterSetType::SVE_HEADER) &&
+ vg_value == GetSVERegVG())
return error;
SetSVERegVG(vg_value);
@@ -687,11 +676,12 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
if (error.Success()) {
// Changing VG during streaming mode also changes the size of ZA.
if (m_sve_state == SVEState::Streaming)
- m_za_header_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::ZA_HEADER);
ConfigureRegisterContext();
}
- if (m_sve_header_is_valid && vg_value == GetSVERegVG())
+ if (m_validity.IsValid(RegisterSetType::SVE_HEADER) &&
+ vg_value == GetSVERegVG())
return error;
}
@@ -828,25 +818,11 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
return Status::FromErrorString("Failed to write register value");
}
-enum RegisterSetType : uint32_t {
- GPR, // General purpose registers.
- SVE, // Used for SVE registers in streaming or non-streaming mode.
- FPR, // When there is no SVE, or SVE in FPSIMD mode, or streaming only SVE
- // that is in non-streaming mode.
- // Pointer authentication registers are read only, so not included here.
- MTE, // Memory tagging control registers.
- TLS, // Thread local storage registers.
- SME, // ZA only, because SVCR and SVG are pseudo registers.
- SME2, // ZT only.
- FPMR, // Floating point mode control registers.
- GCS, // Guarded Control Stack registers.
- POE, // Permission Overlay registers.
-};
-
-static uint8_t *AddRegisterSetType(uint8_t *dst,
- RegisterSetType register_set_type) {
- *(reinterpret_cast<uint32_t *>(dst)) = register_set_type;
- return dst + sizeof(uint32_t);
+uint8_t *NativeRegisterContextLinux_arm64::AddRegisterSetType(
+ uint8_t *dst,
+ NativeRegisterContextLinux_arm64::RegisterSetType register_set_type) {
+ *(reinterpret_cast<RegisterSetType *>(dst)) = register_set_type;
+ return dst + sizeof(RegisterSetType);
}
static uint8_t *AddSavedRegistersData(uint8_t *dst, void *src, size_t size) {
@@ -854,9 +830,10 @@ static uint8_t *AddSavedRegistersData(uint8_t *dst, void *src, size_t size) {
return dst + size;
}
-static uint8_t *AddSavedRegisters(uint8_t *dst,
- enum RegisterSetType register_set_type,
- void *src, size_t size) {
+uint8_t *NativeRegisterContextLinux_arm64::AddSavedRegisters(
+ uint8_t *dst,
+ NativeRegisterContextLinux_arm64::RegisterSetType register_set_type,
+ void *src, size_t size) {
dst = AddRegisterSetType(dst, register_set_type);
return AddSavedRegistersData(dst, src, size);
}
@@ -880,7 +857,7 @@ NativeRegisterContextLinux_arm64::CacheAllRegisters(uint32_t &cached_size) {
cached_size += sizeof(RegisterSetType) + m_za_header.size;
// For the same reason, we need to force it to be re-read so that it will
// always contain the real header.
- m_za_buffer_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::ZA);
error = ReadZA();
if (error.Fail())
return error;
@@ -1010,7 +987,7 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
// Use the header size not the buffer size, as we may be using the buffer
// for fake data, which we do not want to write out.
assert(m_za_header.size <= GetZABufferSize());
- dst = AddSavedRegisters(dst, RegisterSetType::SME, GetZABuffer(),
+ dst = AddSavedRegisters(dst, RegisterSetType::ZA, GetZABuffer(),
m_za_header.size);
}
@@ -1027,7 +1004,7 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
if ((m_sve_state == SVEState::Streaming) && GetRegisterInfo().IsZAPresent()) {
assert(m_za_header.size <= GetZABufferSize());
- dst = AddSavedRegisters(dst, RegisterSetType::SME, GetZABuffer(),
+ dst = AddSavedRegisters(dst, RegisterSetType::ZA, GetZABuffer(),
m_za_header.size);
}
@@ -1041,7 +1018,7 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
GetRegisterInfo().IsZTPresent() &&
// And ZA is enabled.
m_za_header.size > sizeof(m_za_header))
- dst = AddSavedRegisters(dst, RegisterSetType::SME2, GetZTBuffer(),
+ dst = AddSavedRegisters(dst, RegisterSetType::ZT, GetZTBuffer(),
GetZTBufferSize());
if (GetRegisterInfo().IsMTEPresent()) {
@@ -1070,10 +1047,12 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
return error;
}
-static Status RestoreRegisters(void *buffer, const uint8_t **src, size_t len,
- bool &is_valid, std::function<Status()> writer) {
+Status NativeRegisterContextLinux_arm64::RestoreRegisters(
+ void *buffer, const uint8_t **src, size_t len,
+ const NativeRegisterContextLinux_arm64::RegisterSetType set,
+ std::function<Status()> writer) {
::memcpy(buffer, *src, len);
- is_valid = true;
+ m_validity.MakeValid(set);
*src += len;
return writer();
}
@@ -1128,7 +1107,7 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
switch (kind) {
case RegisterSetType::GPR:
error = RestoreRegisters(
- GetGPRBuffer(), &src, GetGPRBufferSize(), m_gpr_is_valid,
+ GetGPRBuffer(), &src, GetGPRBufferSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WriteGPR, this));
break;
case RegisterSetType::SVE:
@@ -1140,14 +1119,14 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
// not want src to be modified yet.
::memcpy(GetSVEHeader(), src, GetSVEHeaderSize());
if (!sve::vl_valid(m_sve_header.vl)) {
- m_sve_header_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::SVE_HEADER);
error = Status::FromErrorStringWithFormat(
"NativeRegisterContextLinux_arm64::%s "
"Invalid SVE header in data_sp",
__FUNCTION__);
return error;
}
- m_sve_header_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::SVE_HEADER);
error = WriteSVEHeader();
if (error.Fail())
return error;
@@ -1159,12 +1138,11 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
// Write header and register data, incrementing src this time.
error = RestoreRegisters(
- GetSVEBuffer(), &src, GetSVEBufferSize(), m_sve_buffer_is_valid,
+ GetSVEBuffer(), &src, GetSVEBufferSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WriteAllSVE, this));
break;
case RegisterSetType::FPR: {
- m_sve_buffer_is_valid = false;
- m_sve_header_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::SVE_HEADER, RegisterSetType::SVE);
m_sve_state = SVEState::Unknown;
ConfigureRegisterContext();
@@ -1209,14 +1187,21 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
// exiting streaming mode.
error = WriteRegisterSet(&ioVec, sve_fpsimd_data.size(), NT_ARM_SVE);
+ // Wrote FPU, and SVE overlaps FPU.
+ m_validity.Invalidate(RegisterSetType::FPR, RegisterSetType::SVE_HEADER,
+ RegisterSetType::SVE);
+
+ m_sve_state = SVEState::Unknown;
+ ConfigureRegisterContext();
+
// Consume FP register set.
src += GetFPRSize();
if (error.Success()) {
// Wrote FPU, and SVE overlaps FPU.
- m_fpu_is_valid = false;
- m_sve_buffer_is_valid = false;
- m_sve_header_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::FPR,
+ RegisterSetType::SVE_HEADER,
+ RegisterSetType::SVE);
m_sve_state = SVEState::Unknown;
ConfigureRegisterContext();
@@ -1225,22 +1210,22 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
// the others.
} else {
error = RestoreRegisters(
- GetFPRBuffer(), &src, GetFPRSize(), m_fpu_is_valid,
+ GetFPRBuffer(), &src, GetFPRSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WriteFPR, this));
}
break;
}
case RegisterSetType::MTE:
error = RestoreRegisters(
- GetMTEControl(), &src, GetMTEControlSize(), m_mte_ctrl_is_valid,
+ GetMTEControl(), &src, GetMTEControlSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WriteMTEControl, this));
break;
case RegisterSetType::TLS:
error = RestoreRegisters(
- GetTLSBuffer(), &src, GetTLSBufferSize(), m_tls_is_valid,
+ GetTLSBuffer(), &src, GetTLSBufferSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WriteTLS, this));
break;
- case RegisterSetType::SME:
+ case RegisterSetType::ZA:
// To enable or disable ZA you write the regset with or without register
// data. The kernel detects this by looking at the ioVec's length, not the
// ZA header size you pass in. Therefore we must write header and register
@@ -1252,7 +1237,7 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
// this so WriteZA uses the correct size.
m_za_ptrace_payload.resize(m_za_header.size);
::memcpy(GetZABuffer(), src, GetZABufferSize());
- m_za_buffer_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::ZA);
error = WriteZA();
if (error.Fail())
@@ -1267,17 +1252,17 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
error = ReadZA();
src += GetZABufferSize();
break;
- case RegisterSetType::SME2:
+ case RegisterSetType::ZT:
// Doing this would activate an inactive ZA, however we will only get here
// if the state we are restoring had an active ZA. Restoring ZT0 will
// always come after restoring ZA.
error = RestoreRegisters(
- GetZTBuffer(), &src, GetZTBufferSize(), m_zt_buffer_is_valid,
+ GetZTBuffer(), &src, GetZTBufferSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WriteZT, this));
break;
case RegisterSetType::FPMR:
error = RestoreRegisters(
- GetFPMRBuffer(), &src, GetFPMRBufferSize(), m_fpmr_is_valid,
+ GetFPMRBuffer(), &src, GetFPMRBufferSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WriteFPMR, this));
break;
case RegisterSetType::GCS: {
@@ -1285,7 +1270,7 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
// to keep things simple we will not revert any change to the
// PR_SHADOW_STACK_ENABLE bit. Instead patch in the current enable bit
// into the registers we are about to restore.
- m_gcs_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::GCS);
error = ReadGCS();
if (error.Fail())
return error;
@@ -1298,7 +1283,7 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
const uint8_t *new_gcs_src =
reinterpret_cast<const uint8_t *>(&new_gcs_regs);
error = RestoreRegisters(
- GetGCSBuffer(), &new_gcs_src, GetGCSBufferSize(), m_gcs_is_valid,
+ GetGCSBuffer(), &new_gcs_src, GetGCSBufferSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WriteGCS, this));
src += GetGCSBufferSize();
@@ -1306,9 +1291,14 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
}
case RegisterSetType::POE:
error = RestoreRegisters(
- GetPOEBuffer(), &src, GetPOEBufferSize(), m_poe_is_valid,
+ GetPOEBuffer(), &src, GetPOEBufferSize(), kind,
std::bind(&NativeRegisterContextLinux_arm64::WritePOE, this));
break;
+ case RegisterSetType::PAC:
+ case RegisterSetType::SVE_HEADER:
+ case RegisterSetType::ZA_HEADER:
+ // These are not saved or restored.
+ break;
}
if (error.Fail())
@@ -1394,7 +1384,7 @@ NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(DREGType hwbType) {
Status NativeRegisterContextLinux_arm64::ReadGPR() {
Status error;
- if (m_gpr_is_valid)
+ if (m_validity.IsValid(RegisterSetType::GPR))
return error;
struct iovec ioVec;
@@ -1404,7 +1394,7 @@ Status NativeRegisterContextLinux_arm64::ReadGPR() {
error = ReadRegisterSet(&ioVec, GetGPRBufferSize(), NT_PRSTATUS);
if (error.Success())
- m_gpr_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::GPR);
return error;
}
@@ -1418,7 +1408,7 @@ Status NativeRegisterContextLinux_arm64::WriteGPR() {
ioVec.iov_base = GetGPRBuffer();
ioVec.iov_len = GetGPRBufferSize();
- m_gpr_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::GPR);
return WriteRegisterSet(&ioVec, GetGPRBufferSize(), NT_PRSTATUS);
}
@@ -1426,7 +1416,7 @@ Status NativeRegisterContextLinux_arm64::WriteGPR() {
Status NativeRegisterContextLinux_arm64::ReadFPR() {
Status error;
- if (m_fpu_is_valid)
+ if (m_validity.IsValid(RegisterSetType::FPR))
return error;
struct iovec ioVec;
@@ -1435,7 +1425,7 @@ Status NativeRegisterContextLinux_arm64::ReadFPR() {
error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
if (error.Success())
- m_fpu_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::FPR);
return error;
}
@@ -1449,28 +1439,15 @@ Status NativeRegisterContextLinux_arm64::WriteFPR() {
ioVec.iov_base = GetFPRBuffer();
ioVec.iov_len = GetFPRSize();
- m_fpu_is_valid = false;
// SVE Z registers overlap the FP registers.
- m_sve_buffer_is_valid = false;
- m_sve_header_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::FPR, RegisterSetType::SVE_HEADER,
+ RegisterSetType::SVE);
return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
}
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;
- m_za_buffer_is_valid = false;
- m_za_header_is_valid = false;
- m_pac_mask_is_valid = false;
- m_mte_ctrl_is_valid = false;
- m_tls_is_valid = false;
- m_zt_buffer_is_valid = false;
- m_fpmr_is_valid = false;
- m_gcs_is_valid = false;
- m_poe_is_valid = false;
+ m_validity = CacheValidity();
// Update SVE and ZA registers in case there is change in configuration.
ConfigureRegisterContext();
@@ -1489,7 +1466,7 @@ unsigned NativeRegisterContextLinux_arm64::GetSVERegSet() {
Status NativeRegisterContextLinux_arm64::ReadSVEHeader() {
Status error;
- if (m_sve_header_is_valid)
+ if (m_validity.IsValid(RegisterSetType::SVE_HEADER))
return error;
struct iovec ioVec;
@@ -1499,7 +1476,7 @@ Status NativeRegisterContextLinux_arm64::ReadSVEHeader() {
error = ReadRegisterSet(&ioVec, GetSVEHeaderSize(), GetSVERegSet());
if (error.Success())
- m_sve_header_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::SVE_HEADER);
return error;
}
@@ -1507,7 +1484,7 @@ Status NativeRegisterContextLinux_arm64::ReadSVEHeader() {
Status NativeRegisterContextLinux_arm64::ReadPAuthMask() {
Status error;
- if (m_pac_mask_is_valid)
+ if (m_validity.IsValid(RegisterSetType::PAC))
return error;
struct iovec ioVec;
@@ -1517,7 +1494,7 @@ Status NativeRegisterContextLinux_arm64::ReadPAuthMask() {
error = ReadRegisterSet(&ioVec, GetPACMaskSize(), NT_ARM_PAC_MASK);
if (error.Success())
- m_pac_mask_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::PAC);
return error;
}
@@ -1533,16 +1510,15 @@ Status NativeRegisterContextLinux_arm64::WriteSVEHeader() {
ioVec.iov_base = GetSVEHeader();
ioVec.iov_len = GetSVEHeaderSize();
- m_sve_buffer_is_valid = false;
- m_sve_header_is_valid = false;
- m_fpu_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::FPR, RegisterSetType::SVE_HEADER,
+ RegisterSetType::SVE);
return WriteRegisterSet(&ioVec, GetSVEHeaderSize(), GetSVERegSet());
}
Status NativeRegisterContextLinux_arm64::ReadAllSVE() {
Status error;
- if (m_sve_buffer_is_valid)
+ if (m_validity.IsValid(RegisterSetType::SVE))
return error;
struct iovec ioVec;
@@ -1552,7 +1528,7 @@ Status NativeRegisterContextLinux_arm64::ReadAllSVE() {
error = ReadRegisterSet(&ioVec, GetSVEBufferSize(), GetSVERegSet());
if (error.Success())
- m_sve_buffer_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::SVE);
return error;
}
@@ -1569,9 +1545,8 @@ Status NativeRegisterContextLinux_arm64::WriteAllSVE() {
ioVec.iov_base = GetSVEBuffer();
ioVec.iov_len = GetSVEBufferSize();
- m_sve_buffer_is_valid = false;
- m_sve_header_is_valid = false;
- m_fpu_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::FPR, RegisterSetType::SVE_HEADER,
+ RegisterSetType::SVE);
return WriteRegisterSet(&ioVec, GetSVEBufferSize(), GetSVERegSet());
}
@@ -1597,7 +1572,7 @@ Status NativeRegisterContextLinux_arm64::ReadSMEControl() {
Status NativeRegisterContextLinux_arm64::ReadMTEControl() {
Status error;
- if (m_mte_ctrl_is_valid)
+ if (m_validity.IsValid(RegisterSetType::MTE))
return error;
struct iovec ioVec;
@@ -1607,7 +1582,7 @@ Status NativeRegisterContextLinux_arm64::ReadMTEControl() {
error = ReadRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL);
if (error.Success())
- m_mte_ctrl_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::MTE);
return error;
}
@@ -1623,7 +1598,7 @@ Status NativeRegisterContextLinux_arm64::WriteMTEControl() {
ioVec.iov_base = GetMTEControl();
ioVec.iov_len = GetMTEControlSize();
- m_mte_ctrl_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::MTE);
return WriteRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL);
}
@@ -1631,7 +1606,7 @@ Status NativeRegisterContextLinux_arm64::WriteMTEControl() {
Status NativeRegisterContextLinux_arm64::ReadTLS() {
Status error;
- if (m_tls_is_valid)
+ if (m_validity.IsValid(RegisterSetType::TLS))
return error;
struct iovec ioVec;
@@ -1641,7 +1616,7 @@ Status NativeRegisterContextLinux_arm64::ReadTLS() {
error = ReadRegisterSet(&ioVec, GetTLSBufferSize(), NT_ARM_TLS);
if (error.Success())
- m_tls_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::TLS);
return error;
}
@@ -1657,7 +1632,7 @@ Status NativeRegisterContextLinux_arm64::WriteTLS() {
ioVec.iov_base = GetTLSBuffer();
ioVec.iov_len = GetTLSBufferSize();
- m_tls_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::TLS);
return WriteRegisterSet(&ioVec, GetTLSBufferSize(), NT_ARM_TLS);
}
@@ -1665,7 +1640,7 @@ Status NativeRegisterContextLinux_arm64::WriteTLS() {
Status NativeRegisterContextLinux_arm64::ReadGCS() {
Status error;
- if (m_gcs_is_valid)
+ if (m_validity.IsValid(RegisterSetType::GCS))
return error;
struct iovec ioVec;
@@ -1675,7 +1650,7 @@ Status NativeRegisterContextLinux_arm64::ReadGCS() {
error = ReadRegisterSet(&ioVec, GetGCSBufferSize(), NT_ARM_GCS);
if (error.Success())
- m_gcs_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::GCS);
return error;
}
@@ -1691,7 +1666,7 @@ Status NativeRegisterContextLinux_arm64::WriteGCS() {
ioVec.iov_base = GetGCSBuffer();
ioVec.iov_len = GetGCSBufferSize();
- m_gcs_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::GCS);
return WriteRegisterSet(&ioVec, GetGCSBufferSize(), NT_ARM_GCS);
}
@@ -1699,7 +1674,7 @@ Status NativeRegisterContextLinux_arm64::WriteGCS() {
Status NativeRegisterContextLinux_arm64::ReadZAHeader() {
Status error;
- if (m_za_header_is_valid)
+ if (m_validity.IsValid(RegisterSetType::ZA_HEADER))
return error;
struct iovec ioVec;
@@ -1709,7 +1684,7 @@ Status NativeRegisterContextLinux_arm64::ReadZAHeader() {
error = ReadRegisterSet(&ioVec, GetZAHeaderSize(), NT_ARM_ZA);
if (error.Success())
- m_za_header_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::ZA_HEADER);
return error;
}
@@ -1717,7 +1692,7 @@ Status NativeRegisterContextLinux_arm64::ReadZAHeader() {
Status NativeRegisterContextLinux_arm64::ReadZA() {
Status error;
- if (m_za_buffer_is_valid)
+ if (m_validity.IsValid(RegisterSetType::ZA))
return error;
struct iovec ioVec;
@@ -1727,7 +1702,7 @@ Status NativeRegisterContextLinux_arm64::ReadZA() {
error = ReadRegisterSet(&ioVec, GetZABufferSize(), NT_ARM_ZA);
if (error.Success())
- m_za_buffer_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::ZA);
return error;
}
@@ -1746,10 +1721,10 @@ Status NativeRegisterContextLinux_arm64::WriteZA() {
ioVec.iov_base = GetZABuffer();
ioVec.iov_len = GetZABufferSize();
- m_za_buffer_is_valid = false;
- m_za_header_is_valid = false;
- // Writing to ZA may enable ZA, which means ZT0 may change too.
- m_zt_buffer_is_valid = false;
+ m_validity.Invalidate(
+ RegisterSetType::ZA_HEADER, RegisterSetType::ZA,
+ // Writing to ZA may enable ZA, which means ZT0 may change too.
+ RegisterSetType::ZT);
return WriteRegisterSet(&ioVec, GetZABufferSize(), NT_ARM_ZA);
}
@@ -1757,7 +1732,7 @@ Status NativeRegisterContextLinux_arm64::WriteZA() {
Status NativeRegisterContextLinux_arm64::ReadZT() {
Status error;
- if (m_zt_buffer_is_valid)
+ if (m_validity.IsValid(RegisterSetType::ZT))
return error;
struct iovec ioVec;
@@ -1765,7 +1740,8 @@ Status NativeRegisterContextLinux_arm64::ReadZT() {
ioVec.iov_len = GetZTBufferSize();
error = ReadRegisterSet(&ioVec, GetZTBufferSize(), NT_ARM_ZT);
- m_zt_buffer_is_valid = error.Success();
+ if (error.Success())
+ m_validity.MakeValid(RegisterSetType::ZT);
return error;
}
@@ -1781,11 +1757,10 @@ Status NativeRegisterContextLinux_arm64::WriteZT() {
ioVec.iov_base = GetZTBuffer();
ioVec.iov_len = GetZTBufferSize();
- m_zt_buffer_is_valid = false;
- // Writing to an inactive ZT0 will enable ZA as well, which invalidates our
- // current copy of it.
- m_za_buffer_is_valid = false;
- m_za_header_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::ZT,
+ // Writing to an inactive ZT0 will enable ZA as well,
+ // which invalidates our current copy of it.
+ RegisterSetType::ZA_HEADER, RegisterSetType::ZA);
return WriteRegisterSet(&ioVec, GetZTBufferSize(), NT_ARM_ZT);
}
@@ -1793,7 +1768,7 @@ Status NativeRegisterContextLinux_arm64::WriteZT() {
Status NativeRegisterContextLinux_arm64::ReadFPMR() {
Status error;
- if (m_fpmr_is_valid)
+ if (m_validity.IsValid(RegisterSetType::FPMR))
return error;
struct iovec ioVec;
@@ -1803,7 +1778,7 @@ Status NativeRegisterContextLinux_arm64::ReadFPMR() {
error = ReadRegisterSet(&ioVec, GetFPMRBufferSize(), NT_ARM_FPMR);
if (error.Success())
- m_fpmr_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::FPMR);
return error;
}
@@ -1819,7 +1794,7 @@ Status NativeRegisterContextLinux_arm64::WriteFPMR() {
ioVec.iov_base = GetFPMRBuffer();
ioVec.iov_len = GetFPMRBufferSize();
- m_fpmr_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::FPMR);
return WriteRegisterSet(&ioVec, GetFPMRBufferSize(), NT_ARM_FPMR);
}
@@ -1827,7 +1802,7 @@ Status NativeRegisterContextLinux_arm64::WriteFPMR() {
Status NativeRegisterContextLinux_arm64::ReadPOE() {
Status error;
- if (m_poe_is_valid)
+ if (m_validity.IsValid(RegisterSetType::POE))
return error;
struct iovec ioVec;
@@ -1837,7 +1812,7 @@ Status NativeRegisterContextLinux_arm64::ReadPOE() {
error = ReadRegisterSet(&ioVec, GetPOEBufferSize(), NT_ARM_POE);
if (error.Success())
- m_poe_is_valid = true;
+ m_validity.MakeValid(RegisterSetType::POE);
return error;
}
@@ -1853,7 +1828,7 @@ Status NativeRegisterContextLinux_arm64::WritePOE() {
ioVec.iov_base = GetPOEBuffer();
ioVec.iov_len = GetPOEBufferSize();
- m_poe_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::POE);
return WriteRegisterSet(&ioVec, GetPOEBufferSize(), NT_ARM_POE);
}
@@ -1864,14 +1839,14 @@ void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
// streaming SVE mode.
// If m_sve_state is set to SVEState::Disabled on first stop, code below will
// be deemed non operational for the lifetime of current process.
- if (!m_sve_header_is_valid && m_sve_state != SVEState::Disabled) {
+ if (!m_validity.IsValid(RegisterSetType::SVE_HEADER) &&
+ m_sve_state != SVEState::Disabled) {
// Systems may have SVE and/or SME. If they are SME only, the SVE regset
// cannot be read from but the SME one can. If they have both SVE and SME,
// only the active mode will return valid register data.
// Check for SME.
- m_sve_header_is_valid = false;
- m_sve_buffer_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::SVE_HEADER, RegisterSetType::SVE);
m_sve_state = SVEState::Streaming;
Status error = ReadSVEHeader();
@@ -1881,8 +1856,7 @@ void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
((m_sve_header.flags & sve::ptrace_regs_mask) == sve::ptrace_regs_sve);
// Check for SVE.
- m_sve_header_is_valid = false;
- m_sve_buffer_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::SVE_HEADER, RegisterSetType::SVE);
m_sve_state = SVEState::Full;
error = ReadSVEHeader();
@@ -1911,9 +1885,7 @@ void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
if (m_sve_state == SVEState::Full || m_sve_state == SVEState::FPSIMD ||
m_sve_state == SVEState::Streaming ||
m_sve_state == SVEState::StreamingFPSIMD) {
-
- m_sve_header_is_valid = false;
- m_sve_buffer_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::SVE_HEADER, RegisterSetType::SVE);
error = ReadSVEHeader();
// On every stop we configure SVE vector length by calling
@@ -1927,7 +1899,7 @@ void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
}
}
- if (!m_za_header_is_valid) {
+ if (!m_validity.IsValid(RegisterSetType::ZA_HEADER)) {
Status error = ReadZAHeader();
if (error.Success()) {
uint32_t vq = RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64SVE;
@@ -1936,7 +1908,7 @@ void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
GetRegisterInfo().ConfigureVectorLengthZA(vq);
m_za_ptrace_payload.resize(m_za_header.size);
- m_za_buffer_is_valid = false;
+ m_validity.Invalidate(RegisterSetType::ZA);
}
}
}
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
index e2146f3ba6d2c..703adcd78bd11 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -79,21 +79,66 @@ class NativeRegisterContextLinux_arm64
lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) override;
private:
- bool m_gpr_is_valid;
- bool m_fpu_is_valid;
- bool m_sve_buffer_is_valid;
- bool m_mte_ctrl_is_valid;
- bool m_zt_buffer_is_valid;
- bool m_fpmr_is_valid;
-
- bool m_sve_header_is_valid;
- bool m_za_buffer_is_valid;
- bool m_za_header_is_valid;
- bool m_pac_mask_is_valid;
- bool m_tls_is_valid;
- size_t m_tls_size;
- bool m_gcs_is_valid;
- bool m_poe_is_valid;
+ // Bit mask enum used to refer to the types of registers we support. Currently
+ // used for tracking cache validity and ReadAll/WriteAllRegister data. Will
+ // be used for much more in future.
+ enum RegisterSetType : uint32_t {
+ GPR = 1 << 0, // General purpose registers.
+ FPR = 1 << 1, // When there is no SVE, or SVE in FPSIMD mode, or streaming
+ // only SVE that is in non-streaming mode.
+ SVE = 1 << 2, // Used for SVE registers in streaming or non-streaming mode.
+ SVE_HEADER = 1 << 3, // Only the ptrace header for SVE.
+ PAC = 1 << 4, // Pointer authentication mask registers.
+ MTE = 1 << 5, // Memory tagging control registers.
+ TLS = 1 << 6, // Thread local storage registers.
+ ZA = 1 << 7, // ZA only, because SVCR and SVG are pseudo registers.
+ ZA_HEADER = 1 << 8, // Only the ptrace header for ZA.
+ ZT = 1 << 9, // ZT only.
+ FPMR = 1 << 10, // Floating point mode control registers.
+ GCS = 1 << 11, // Guarded Control Stack registers.
+ POE = 1 << 12, // Permission Overlay registers.
+ };
+
+ // This single object manages all tracking of whether register value caches
+ // are valid. Having a single object makes it easy to reset without missing
+ // anything.
+ class CacheValidity {
+ private:
+ using Storage = std::underlying_type_t<RegisterSetType>;
+ Storage m_valid_flags = 0;
+
+ public:
+ void Invalidate(RegisterSetType set) {
+ m_valid_flags &= ~static_cast<Storage>(set);
+ }
+
+ template <typename... Ts>
+ void Invalidate(RegisterSetType first, Ts... rest) {
+ static_assert((std::is_same_v<Ts, RegisterSetType> && ...));
+ Invalidate(first);
+ (Invalidate(rest), ...);
+ }
+
+ void MakeValid(RegisterSetType set) {
+ m_valid_flags |= static_cast<Storage>(set);
+ }
+ bool IsValid(RegisterSetType set) {
+ return (m_valid_flags & static_cast<Storage>(set)) != 0;
+ }
+ } m_validity;
+
+ static uint8_t *AddRegisterSetType(uint8_t *dst,
+ RegisterSetType register_set_type);
+
+ static uint8_t *AddSavedRegisters(uint8_t *dst,
+ RegisterSetType register_set_type,
+ void *src, size_t size);
+
+ Status RestoreRegisters(void *buffer, const uint8_t **src, size_t len,
+ const RegisterSetType set,
+ std::function<Status()> writer);
+
+ size_t m_tls_size = 0;
struct user_pt_regs m_gpr_arm64; // 64-bit general purpose registers.
>From f5175bee3004c091b16e8ba21c48620c1cae749f Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at arm.com>
Date: Tue, 12 May 2026 08:06:39 +0000
Subject: [PATCH 2/2] Remove extra invalidates added by rebase.
---
.../Process/Linux/NativeRegisterContextLinux_arm64.cpp | 7 -------
1 file changed, 7 deletions(-)
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
index 1beb99d0571bd..c1147837301f6 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -1187,13 +1187,6 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
// exiting streaming mode.
error = WriteRegisterSet(&ioVec, sve_fpsimd_data.size(), NT_ARM_SVE);
- // Wrote FPU, and SVE overlaps FPU.
- m_validity.Invalidate(RegisterSetType::FPR, RegisterSetType::SVE_HEADER,
- RegisterSetType::SVE);
-
- m_sve_state = SVEState::Unknown;
- ConfigureRegisterContext();
-
// Consume FP register set.
src += GetFPRSize();
More information about the lldb-commits
mailing list