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 <[email protected]> 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 <[email protected]> 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(); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
