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 @@ -26,8 +26,9 @@ : public NativeRegisterContextLinux, public NativeRegisterContextDBReg_arm64 { public: - NativeRegisterContextLinux_arm64(const ArchSpec &target_arch, - NativeThreadProtocol &native_thread); + NativeRegisterContextLinux_arm64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread, + std::unique_ptr register_info_up); uint32_t GetRegisterSetCount() const override; 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 @@ -46,18 +46,37 @@ case llvm::Triple::arm: return std::make_unique(target_arch, native_thread); - case llvm::Triple::aarch64: - return std::make_unique(target_arch, - native_thread); + case llvm::Triple::aarch64: { + // Configure register sets supported by this AArch64 target. + // Read SVE header to check for SVE support. + struct user_sve_header sve_header; + struct iovec ioVec; + ioVec.iov_base = &sve_header; + ioVec.iov_len = sizeof(sve_header); + unsigned int regset = NT_ARM_SVE; + + Flags opt_regsets; + if (NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, + native_thread.GetID(), ®set, + &ioVec, sizeof(sve_header)) + .Success()) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE); + + auto register_info_up = + std::make_unique(target_arch, opt_regsets); + return std::make_unique( + target_arch, native_thread, std::move(register_info_up)); + } default: llvm_unreachable("have no register context for architecture"); } } NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64( - const ArchSpec &target_arch, NativeThreadProtocol &native_thread) - : NativeRegisterContextRegisterInfo( - native_thread, new RegisterInfoPOSIX_arm64(target_arch)) { + const ArchSpec &target_arch, NativeThreadProtocol &native_thread, + std::unique_ptr register_info_up) + : NativeRegisterContextRegisterInfo(native_thread, + register_info_up.release()) { ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64)); ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); @@ -75,8 +94,10 @@ m_sve_buffer_is_valid = false; m_sve_header_is_valid = false; - // SVE is not enabled until we query user_sve_header - m_sve_state = SVEState::Unknown; + if (GetRegisterInfo().IsSVEEnabled()) + m_sve_state = SVEState::Unknown; + else + m_sve_state = SVEState::Disabled; } RegisterInfoPOSIX_arm64 & @@ -451,10 +472,7 @@ } bool NativeRegisterContextLinux_arm64::IsSVE(unsigned reg) const { - if (GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) == - RegisterInfoPOSIX_arm64::SVERegSet) - return true; - return false; + return GetRegisterInfo().IsSVEReg(reg); } llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { @@ -676,23 +694,27 @@ } void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() { - // Read SVE configuration data and configure register infos. + // ConfigureRegisterContext gets called from InvalidateAllRegisters + // on every stop and configures SVE vector length. + // 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) { Status error = ReadSVEHeader(); - if (!error.Success() && m_sve_state == SVEState::Unknown) { - m_sve_state = SVEState::Disabled; - GetRegisterInfo().ConfigureVectorRegisterInfos( - RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64); - } else { + if (error.Success()) { + // If SVE is enabled thread can switch between SVEState::FPSIMD and + // SVEState::Full on every stop. if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) m_sve_state = SVEState::FPSIMD; else if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE) m_sve_state = SVEState::Full; + // On every stop we configure SVE vector length by calling + // ConfigureVectorLength regardless of current SVEState of this thread. uint32_t vq = RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64SVE; if (sve_vl_valid(m_sve_header.vl)) vq = sve_vq_from_vl(m_sve_header.vl); - GetRegisterInfo().ConfigureVectorRegisterInfos(vq); + + GetRegisterInfo().ConfigureVectorLength(vq); m_sve_ptrace_payload.resize(SVE_PT_SIZE(vq, SVE_PT_REGS_SVE)); } } Index: lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp =================================================================== --- lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp +++ lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp @@ -40,10 +40,7 @@ } bool RegisterContextPOSIX_arm64::IsSVE(unsigned reg) const { - if (m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == - RegisterInfoPOSIX_arm64::SVERegSet) - return true; - return false; + return m_register_info_up->IsSVEReg(reg); } RegisterContextPOSIX_arm64::RegisterContextPOSIX_arm64( Index: lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h =================================================================== --- lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h +++ lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h @@ -11,6 +11,7 @@ #include "RegisterInfoAndSetInterface.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Flags.h" #include "lldb/lldb-private.h" #include @@ -19,7 +20,14 @@ class RegisterInfoPOSIX_arm64 : public lldb_private::RegisterInfoAndSetInterface { public: - enum { GPRegSet = 0, FPRegSet, SVERegSet }; + enum { GPRegSet = 0, FPRegSet }; + + // AArch64 register set mask value + enum { + eRegsetMaskDefault = 0, + eRegsetMaskSVE = 1, + eRegsetMaskDynamic = ~1, + }; // AArch64 Register set FP/SIMD feature configuration enum { @@ -68,7 +76,8 @@ uint64_t mdscr_el1; }; - RegisterInfoPOSIX_arm64(const lldb_private::ArchSpec &target_arch); + RegisterInfoPOSIX_arm64(const lldb_private::ArchSpec &target_arch, + lldb_private::Flags opt_regsets); size_t GetGPRSize() const override; @@ -85,7 +94,7 @@ size_t GetRegisterSetFromRegisterIndex(uint32_t reg_index) const override; - uint32_t ConfigureVectorRegisterInfos(uint32_t sve_vq); + uint32_t ConfigureVectorLength(uint32_t sve_vq); bool VectorSizeIsValid(uint32_t vq) { if (vq >= eVectorQuadwordAArch64 && vq <= eVectorQuadwordAArch64SVEMax) @@ -93,8 +102,9 @@ return false; } - bool IsSVEEnabled() const { return m_vector_reg_vq > eVectorQuadwordAArch64; } + bool IsSVEEnabled() const { return m_opt_regsets.AnySet(eRegsetMaskSVE); } + bool IsSVEReg(unsigned reg) const; bool IsSVEZReg(unsigned reg) const; bool IsSVEPReg(unsigned reg) const; bool IsSVERegVG(unsigned reg) const; @@ -115,6 +125,18 @@ const lldb_private::RegisterInfo *m_register_info_p; uint32_t m_register_info_count; + + const lldb_private::RegisterSet *m_register_set_p; + uint32_t m_register_set_count; + + // Contains pair of [start, end] register numbers of a register set with start + // and end included. + std::map> m_per_regset_regnum_range; + + lldb_private::Flags m_opt_regsets; + + std::vector m_dynamic_reg_infos; + std::vector m_dynamic_reg_sets; }; #endif Index: lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp =================================================================== --- lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp +++ lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp @@ -72,23 +72,12 @@ #include "RegisterInfos_arm64_sve.h" #undef DECLARE_REGISTER_INFOS_ARM64_STRUCT -static const lldb_private::RegisterInfo * -GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { - switch (target_arch.GetMachine()) { - case llvm::Triple::aarch64: - case llvm::Triple::aarch64_32: - return g_register_infos_arm64_le; - default: - assert(false && "Unhandled target architecture."); - return nullptr; - } -} - // Number of register sets provided by this context. enum { k_num_gpr_registers = gpr_w28 - gpr_x0 + 1, k_num_fpr_registers = fpu_fpcr - fpu_v0 + 1, k_num_sve_registers = sve_ffr - sve_vg + 1, + k_num_register_sets_default = 2, k_num_register_sets = 3 }; @@ -186,31 +175,54 @@ {"Scalable Vector Extension Registers", "sve", k_num_sve_registers, g_sve_regnums_arm64}}; -static uint32_t -GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) { +RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64( + const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets) + : lldb_private::RegisterInfoAndSetInterface(target_arch), + m_opt_regsets(opt_regsets) { switch (target_arch.GetMachine()) { case llvm::Triple::aarch64: - case llvm::Triple::aarch64_32: - return static_cast(sizeof(g_register_infos_arm64_le) / - sizeof(g_register_infos_arm64_le[0])); + case llvm::Triple::aarch64_32: { + m_register_set_p = g_reg_sets_arm64; + m_register_set_count = k_num_register_sets_default; + m_per_regset_regnum_range[GPRegSet] = std::make_pair(gpr_x0, gpr_w28 + 1); + m_per_regset_regnum_range[FPRegSet] = std::make_pair(fpu_v0, fpu_fpcr + 1); + + // Now configure register sets supported by current target. If we have a + // dynamic register set like MTE, Pointer Authentication regset then we need + // to create dynamic register infos and regset array. Push back all optional + // register infos and regset and calculate register offsets accordingly. + if (m_opt_regsets.AllSet(eRegsetMaskSVE)) { + m_register_info_p = g_register_infos_arm64_sve_le; + m_register_info_count = sve_ffr + 1; + m_per_regset_regnum_range[m_register_set_count++] = + std::make_pair(sve_vg, sve_ffr + 1); + } else { + m_register_info_p = g_register_infos_arm64_le; + m_register_info_count = fpu_fpcr + 1; + } + + if (m_opt_regsets.AnySet(eRegsetMaskDynamic)) { + llvm::ArrayRef reg_infos_ref = + llvm::makeArrayRef(m_register_info_p, m_register_info_count); + llvm::ArrayRef reg_sets_ref = + llvm::makeArrayRef(m_register_set_p, m_register_set_count); + llvm::copy(reg_infos_ref, std::back_inserter(m_dynamic_reg_infos)); + llvm::copy(reg_sets_ref, std::back_inserter(m_dynamic_reg_sets)); + + m_register_info_count = m_dynamic_reg_infos.size(); + m_register_info_p = m_dynamic_reg_infos.data(); + m_register_set_p = m_dynamic_reg_sets.data(); + m_register_set_count = m_dynamic_reg_sets.size(); + } + break; + } default: assert(false && "Unhandled target architecture."); - return 0; } } -RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64( - const lldb_private::ArchSpec &target_arch) - : lldb_private::RegisterInfoAndSetInterface(target_arch), - m_register_info_p(GetRegisterInfoPtr(target_arch)), - m_register_info_count(GetRegisterInfoCount(target_arch)) { -} - uint32_t RegisterInfoPOSIX_arm64::GetRegisterCount() const { - if (IsSVEEnabled()) - return k_num_gpr_registers + k_num_fpr_registers + k_num_sve_registers; - - return k_num_gpr_registers + k_num_fpr_registers; + return m_register_info_count; } size_t RegisterInfoPOSIX_arm64::GetGPRSize() const { @@ -227,31 +239,27 @@ } size_t RegisterInfoPOSIX_arm64::GetRegisterSetCount() const { - if (IsSVEEnabled()) - return k_num_register_sets; - return k_num_register_sets - 1; + return m_register_set_count; } size_t RegisterInfoPOSIX_arm64::GetRegisterSetFromRegisterIndex( uint32_t reg_index) const { - if (reg_index <= gpr_w28) - return GPRegSet; - if (reg_index <= fpu_fpcr) - return FPRegSet; - if (reg_index <= sve_ffr) - return SVERegSet; + for (const auto ®set_range : m_per_regset_regnum_range) { + if (reg_index >= regset_range.second.first && + reg_index < regset_range.second.second) + return regset_range.first; + } return LLDB_INVALID_REGNUM; } const lldb_private::RegisterSet * RegisterInfoPOSIX_arm64::GetRegisterSet(size_t set_index) const { if (set_index < GetRegisterSetCount()) - return &g_reg_sets_arm64[set_index]; + return &m_register_set_p[set_index]; return nullptr; } -uint32_t -RegisterInfoPOSIX_arm64::ConfigureVectorRegisterInfos(uint32_t sve_vq) { +uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLength(uint32_t sve_vq) { // sve_vq contains SVE Quad vector length in context of AArch64 SVE. // SVE register infos if enabled cannot be disabled by selecting sve_vq = 0. // Also if an invalid or previously set vector length is passed to this @@ -266,28 +274,15 @@ m_vector_reg_vq = sve_vq; - if (sve_vq == eVectorQuadwordAArch64) { - m_register_info_count = - static_cast(sizeof(g_register_infos_arm64_le) / - sizeof(g_register_infos_arm64_le[0])); - m_register_info_p = g_register_infos_arm64_le; - + if (sve_vq == eVectorQuadwordAArch64) return m_vector_reg_vq; - } - - m_register_info_count = - static_cast(sizeof(g_register_infos_arm64_sve_le) / - sizeof(g_register_infos_arm64_sve_le[0])); - std::vector ®_info_ref = m_per_vq_reg_infos[sve_vq]; if (reg_info_ref.empty()) { - reg_info_ref = llvm::makeArrayRef(g_register_infos_arm64_sve_le, - m_register_info_count); + reg_info_ref = llvm::makeArrayRef(m_register_info_p, m_register_info_count); uint32_t offset = SVE_REGS_DEFAULT_OFFSET_LINUX; - reg_info_ref[fpu_fpsr].byte_offset = offset; reg_info_ref[fpu_fpcr].byte_offset = offset + 4; reg_info_ref[sve_vg].byte_offset = offset + 8; @@ -316,13 +311,25 @@ offset += reg_info_ref[it].byte_size; } + for (uint32_t it = sve_ffr + 1; it < m_register_info_count; it++) { + reg_info_ref[it].byte_offset = offset; + offset += reg_info_ref[it].byte_size; + } + m_per_vq_reg_infos[sve_vq] = reg_info_ref; } - m_register_info_p = reg_info_ref.data(); + m_register_info_p = m_per_vq_reg_infos[sve_vq].data(); return m_vector_reg_vq; } +bool RegisterInfoPOSIX_arm64::IsSVEReg(unsigned reg) const { + if (m_vector_reg_vq > eVectorQuadwordAArch64) + return (sve_vg <= reg && reg <= sve_ffr); + else + return false; +} + bool RegisterInfoPOSIX_arm64::IsSVEZReg(unsigned reg) const { return (sve_z0 <= reg && reg <= sve_z31); } Index: lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h =================================================================== --- lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h +++ lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h @@ -18,11 +18,10 @@ class RegisterContextCorePOSIX_arm64 : public RegisterContextPOSIX_arm64 { public: - RegisterContextCorePOSIX_arm64( - lldb_private::Thread &thread, - std::unique_ptr register_info, - const lldb_private::DataExtractor &gpregset, - llvm::ArrayRef notes); + static std::unique_ptr + Create(lldb_private::Thread &thread, const lldb_private::ArchSpec &arch, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef notes); ~RegisterContextCorePOSIX_arm64() override; @@ -39,6 +38,13 @@ bool HardwareSingleStep(bool enable) override; protected: + RegisterContextCorePOSIX_arm64( + lldb_private::Thread &thread, + std::unique_ptr register_info, + const lldb_private::DataExtractor &gpregset, + const lldb_private::DataExtractor &sveregset, + llvm::ArrayRef notes); + bool ReadGPR() override; bool ReadFPR() override; Index: lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp =================================================================== --- lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp +++ lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp @@ -17,10 +17,29 @@ using namespace lldb_private; +std::unique_ptr +RegisterContextCorePOSIX_arm64::Create(Thread &thread, const ArchSpec &arch, + const DataExtractor &gpregset, + llvm::ArrayRef notes) { + DataExtractor sveregset = + getRegset(notes, arch.GetTriple(), AARCH64_SVE_Desc); + + Flags opt_regsets = RegisterInfoPOSIX_arm64::eRegsetMaskDefault; + if (sveregset.GetByteSize() > sizeof(sve::user_sve_header)) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE); + auto register_info_up = + std::make_unique(arch, opt_regsets); + return std::unique_ptr( + new RegisterContextCorePOSIX_arm64(thread, std::move(register_info_up), + gpregset, sveregset, notes)); +} + RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64( Thread &thread, std::unique_ptr register_info, - const DataExtractor &gpregset, llvm::ArrayRef notes) - : RegisterContextPOSIX_arm64(thread, std::move(register_info)) { + const DataExtractor &gpregset, const DataExtractor &sveregset, + llvm::ArrayRef notes) + : RegisterContextPOSIX_arm64(thread, std::move(register_info)), + m_sveregset(sveregset) { m_gpr_buffer = std::make_shared(gpregset.GetDataStart(), gpregset.GetByteSize()); m_gpr.SetData(m_gpr_buffer); @@ -29,10 +48,6 @@ m_fpregset = getRegset( notes, m_register_info_up->GetTargetArchitecture().GetTriple(), FPR_Desc); - m_sveregset = - getRegset(notes, m_register_info_up->GetTargetArchitecture().GetTriple(), - AARCH64_SVE_Desc); - ConfigureRegisterContext(); } @@ -70,15 +85,16 @@ sve::ptrace_regs_sve) m_sve_state = SVEState::Full; - if (sve::vl_valid(m_sve_vector_length)) - m_register_info_up->ConfigureVectorRegisterInfos( - sve::vq_from_vl(m_sve_vector_length)); - else { + if (!sve::vl_valid(m_sve_vector_length)) { m_sve_state = SVEState::Disabled; m_sve_vector_length = 0; } } else m_sve_state = SVEState::Disabled; + + if (m_sve_state != SVEState::Disabled) + m_register_info_up->ConfigureVectorLength( + sve::vq_from_vl(m_sve_vector_length)); } uint32_t RegisterContextCorePOSIX_arm64::CalculateSVEOffset( Index: lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp =================================================================== --- lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -177,9 +177,8 @@ switch (arch.GetMachine()) { case llvm::Triple::aarch64: - m_thread_reg_ctx_sp = std::make_shared( - *this, std::make_unique(arch), - m_gpregset_data, m_notes); + m_thread_reg_ctx_sp = RegisterContextCorePOSIX_arm64::Create( + *this, arch, m_gpregset_data, m_notes); break; case llvm::Triple::arm: m_thread_reg_ctx_sp = std::make_shared(