diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -299,14 +299,31 @@ if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown) return Status("SVE disabled or not supported"); else { - if (GetRegisterInfo().IsSVERegVG(reg)) - return Status("SVE state change operation not supported"); - // Target has SVE enabled, we will read and cache SVE ptrace data error = ReadAllSVE(); if (error.Fail()) return error; + if (GetRegisterInfo().IsSVERegVG(reg)) { + uint64_t vg_value = reg_value.GetAsUInt64(); + + if (sve_vl_valid(vg_value * 8)) { + if (m_sve_header_is_valid && vg_value == GetSVERegVG()) + return error; + + SetSVERegVG(vg_value); + + error = WriteSVEHeader(); + if (error.Success()) + ConfigureRegisterContext(); + + if (m_sve_header_is_valid && vg_value == GetSVERegVG()) + return error; + } + + return Status("SVE vector length update failed."); + } + // If target supports SVE but currently in FPSIMD mode. if (m_sve_state == SVEState::FPSIMD) { // Here we will check if writing this SVE register enables diff --git a/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h b/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h --- a/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h +++ b/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h @@ -66,6 +66,9 @@ bool IsReconfigurable(); + const lldb_private::RegisterInfo * + GetRegisterInfo(llvm::StringRef reg_name) const; + protected: // Classes that inherit from DynamicRegisterInfo can see and modify these typedef std::vector reg_collection; @@ -77,9 +80,6 @@ typedef std::vector dwarf_opcode; typedef std::map dynamic_reg_size_map; - const lldb_private::RegisterInfo * - GetRegisterInfo(llvm::StringRef reg_name) const; - void MoveFrom(DynamicRegisterInfo &&info); void ConfigureOffsets(); diff --git a/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp b/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp --- a/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp +++ b/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp @@ -617,6 +617,17 @@ // targets supporting dynamic offset calculation. It also calculates // total byte size of register data. ConfigureOffsets(); + + // Check if register info is reconfigurable + // AArch64 SVE register set has configurable register sizes + if (arch.GetTriple().isAArch64()) { + for (const auto ® : m_regs) { + if (strcmp(reg.name, "vg") == 0) { + m_is_reconfigurable = true; + break; + } + } + } } void DynamicRegisterInfo::ConfigureOffsets() { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -39,6 +39,7 @@ ~GDBRemoteDynamicRegisterInfo() override = default; void HardcodeARMRegisters(bool from_scratch); + bool UpdateARM64SVERegistersInfos(uint64_t vg); }; class GDBRemoteRegisterContext : public RegisterContext { @@ -77,6 +78,8 @@ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; + bool AArch64SVEReconfigure(); + protected: friend class ThreadGDBRemote; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -214,8 +214,8 @@ for (int i = 0; i < regcount; i++) { struct RegisterInfo *reginfo = m_reg_info_sp->GetRegisterInfoAtIndex(i); - if (reginfo->byte_offset + reginfo->byte_size - <= buffer_sp->GetByteSize()) { + if (reginfo->byte_offset + reginfo->byte_size <= + buffer_sp->GetByteSize()) { m_reg_valid[i] = true; } else { m_reg_valid[i] = false; @@ -344,6 +344,15 @@ if (dst == nullptr) return false; + // Code below is specific to AArch64 target in SVE state + // If vector granule (vg) register is being written then thread's + // register context reconfiguration is triggered on success. + bool do_reconfigure_arm64_sve = false; + const ArchSpec &arch = process->GetTarget().GetArchitecture(); + if (arch.IsValid() && arch.GetTriple().isAArch64()) + if (strcmp(reg_info->name, "vg") == 0) + do_reconfigure_arm64_sve = true; + if (data.CopyByteOrderedData(data_offset, // src offset reg_info->byte_size, // src length dst, // dst @@ -363,6 +372,10 @@ { SetAllRegisterValid(false); + + if (do_reconfigure_arm64_sve) + AArch64SVEReconfigure(); + return true; } } else { @@ -391,6 +404,9 @@ } else { // This is an actual register, write it success = SetPrimordialRegister(reg_info, gdb_comm); + + if (success && do_reconfigure_arm64_sve) + AArch64SVEReconfigure(); } // Check if writing this register will invalidate any other register @@ -656,9 +672,8 @@ if (m_thread.GetProcess().get()) { const ArchSpec &arch = m_thread.GetProcess()->GetTarget().GetArchitecture(); - if (arch.IsValid() && - (arch.GetMachine() == llvm::Triple::aarch64 || - arch.GetMachine() == llvm::Triple::aarch64_32) && + if (arch.IsValid() && (arch.GetMachine() == llvm::Triple::aarch64 || + arch.GetMachine() == llvm::Triple::aarch64_32) && arch.GetTriple().getVendor() == llvm::Triple::Apple && arch.GetTriple().getOS() == llvm::Triple::IOS) { arm64_debugserver = true; @@ -713,6 +728,62 @@ return m_reg_info_sp->ConvertRegisterKindToRegisterNumber(kind, num); } +bool GDBRemoteRegisterContext::AArch64SVEReconfigure() { + if (!m_reg_info_sp) + return false; + + const RegisterInfo *reg_info = m_reg_info_sp->GetRegisterInfo("vg"); + if (!reg_info) + return false; + + uint64_t fail_value = LLDB_INVALID_ADDRESS; + uint32_t vg_reg_num = reg_info->kinds[eRegisterKindLLDB]; + uint64_t vg_reg_value = ReadRegisterAsUnsigned(vg_reg_num, fail_value); + + if (vg_reg_value != fail_value && vg_reg_value <= 32) { + const RegisterInfo *reg_info = m_reg_info_sp->GetRegisterInfo("p0"); + if (!reg_info || vg_reg_value == reg_info->byte_size) + return false; + + if (m_reg_info_sp->UpdateARM64SVERegistersInfos(vg_reg_value)) { + // Make a heap based buffer that is big enough to store all registers + m_reg_data.SetData(std::make_shared( + m_reg_info_sp->GetRegisterDataByteSize(), 0)); + m_reg_data.SetByteOrder(GetByteOrder()); + + InvalidateAllRegisters(); + + return true; + } + } + + return false; +} + +bool GDBRemoteDynamicRegisterInfo::UpdateARM64SVERegistersInfos(uint64_t vg) { + // SVE Z register size is vg x 8 bytes. + uint32_t z_reg_byte_size = vg * 8; + + // SVE vector length has changed, accordingly set size of Z, P and FFR + // registers. Also invalidate register offsets it will be recalculated + // after SVE register size update. + for (auto ® : m_regs) { + if (reg.value_regs == nullptr) { + if (reg.name[0] == 'z' && isdigit(reg.name[1])) + reg.byte_size = z_reg_byte_size; + else if (reg.name[0] == 'p' && isdigit(reg.name[1])) + reg.byte_size = vg; + else if (strcmp(reg.name, "ffr") == 0) + reg.byte_size = vg; + } + reg.byte_offset = LLDB_INVALID_INDEX32; + } + + // Re-calculate register offsets + ConfigureOffsets(); + return true; +} + void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) { // For Advanced SIMD and VFP register mapping. static uint32_t g_d0_regs[] = {26, 27, LLDB_INVALID_REGNUM}; // (s0, s1) diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1763,6 +1763,19 @@ gdb_thread->PrivateSetRegisterValue(pair.first, buffer_sp->GetData()); } + // AArch64 SVE specific code below calls AArch64SVEReconfigure to update + // SVE register sizes and offsets if value of VG register has changed + // since last stop. + const ArchSpec &arch = GetTarget().GetArchitecture(); + if (arch.IsValid() && arch.GetTriple().isAArch64()) { + GDBRemoteRegisterContext *reg_ctx_sp = + static_cast( + gdb_thread->GetRegisterContext().get()); + + if (reg_ctx_sp) + reg_ctx_sp->AArch64SVEReconfigure(); + } + thread_sp->SetName(thread_name.empty() ? nullptr : thread_name.c_str()); gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr);