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 @@ -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 Index: lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp =================================================================== --- lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp +++ lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp @@ -533,6 +533,17 @@ } } + // Create per thread reginfo to support AArch64 SVE dynamic register sizes. + if (arch.GetMachine() == llvm::Triple::aarch64 || + arch.GetMachine() == llvm::Triple::aarch64_be) { + for (const auto ® : m_regs) { + if (strcmp(reg.name, "vg") == 0) { + m_per_thread_reginfo = true; + break; + } + } + } + if (!generic_regs_specified) { switch (arch.GetMachine()) { case llvm::Triple::aarch64: @@ -684,6 +695,7 @@ m_invalidate_regs_map.clear(); m_dynamic_reg_size_map.clear(); m_reg_data_byte_size = 0; + m_per_thread_reginfo = false; m_finalized = false; } Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -40,6 +40,8 @@ void HardcodeARMRegisters(bool from_scratch); + bool UpdateARM64SVERegistersInfos(uint64_t vg, uint32_t &end_reg_offset); + void CloneFrom(GDBRemoteDynamicRegisterInfoSP process_reginfo); }; @@ -79,6 +81,8 @@ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; + bool AArch64SVEReconfigure(); + protected: friend class ThreadGDBRemote; Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -343,6 +343,17 @@ 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.GetMachine() == llvm::Triple::aarch64 || + arch.GetMachine() == llvm::Triple::aarch64_be)) { + if (reg_info->kinds[eRegisterKindDWARF] == arm64_dwarf::vg) + do_reconfigure_arm64_sve = true; + } + if (data.CopyByteOrderedData(data_offset, // src offset reg_info->byte_size, // src length dst, // dst @@ -362,6 +373,11 @@ { SetAllRegisterValid(false); + + if (do_reconfigure_arm64_sve && + GetPrimordialRegister(reg_info, gdb_comm)) + AArch64SVEReconfigure(); + return true; } } else { @@ -390,6 +406,10 @@ } else { // This is an actual register, write it success = SetPrimordialRegister(reg_info, gdb_comm); + + if (success && do_reconfigure_arm64_sve && + GetPrimordialRegister(reg_info, gdb_comm)) + AArch64SVEReconfigure(); } // Check if writing this register will invalidate any other register @@ -712,6 +732,110 @@ return m_reg_info_sp->ConvertRegisterKindToRegisterNumber(kind, num); } +bool GDBRemoteRegisterContext::AArch64SVEReconfigure(void) { + if (!m_reg_info_sp) + return false; + + uint64_t fail_value = LLDB_INVALID_ADDRESS; + uint32_t vg_reg_num = + ConvertRegisterKindToRegisterNumber(eRegisterKindDWARF, arm64_dwarf::vg); + uint64_t vg_reg_value = ReadRegisterAsUnsigned(vg_reg_num, fail_value); + + if (vg_reg_value != fail_value && vg_reg_value <= 32) { + uint32_t end_reg_offset = 0; + if (m_reg_info_sp->UpdateARM64SVERegistersInfos(vg_reg_value, + end_reg_offset)) { + uint64_t bytes_to_copy = m_reg_data.GetByteSize(); + if (end_reg_offset < bytes_to_copy) + bytes_to_copy = end_reg_offset; + + // Make a heap based buffer that is big enough to store all registers + DataBufferSP reg_data_sp(new DataBufferHeap(end_reg_offset, 0)); + m_reg_data.CopyData(0, bytes_to_copy, reg_data_sp->GetBytes()); + m_reg_data.Clear(); + m_reg_data.SetData(reg_data_sp); + m_reg_data.SetByteOrder(GetByteOrder()); + + // Invalidate all register except GPRs and VG. GPRs will retain their + // state. + uint32_t v0_reg_num = + ConvertRegisterKindToRegisterNumber(eRegisterKindDWARF, arm64_dwarf::v0); + for (uint16_t i = v0_reg_num; i < vg_reg_num; i++) + m_reg_valid[i] = false; + + for (uint16_t i = vg_reg_num + 1; i < m_reg_valid.size(); i++) + m_reg_valid[i] = false; + + return true; + } + } + + return false; +} + +bool GDBRemoteDynamicRegisterInfo::UpdateARM64SVERegistersInfos( + uint64_t vg, uint32_t &end_reg_offset) { + uint32_t z0_reg_num = + ConvertRegisterKindToRegisterNumber(eRegisterKindDWARF, arm64_dwarf::z0); + RegisterInfo *reg_info = GetRegisterInfoAtIndex(z0_reg_num); + uint64_t vg_reg_value_old = reg_info->byte_size / 8; + + if (vg_reg_value_old != vg) { + uint32_t s_reg_offset = reg_info->byte_offset; + uint32_t d_reg_offset = reg_info->byte_offset; + uint32_t v_reg_offset = reg_info->byte_offset; + uint32_t z_reg_offset = reg_info->byte_offset; + + // SVE Z register size is vg x 8 bytes. + uint32_t z_reg_byte_size = vg * 8; + + // To calculate fpsr and fpcr offset we skip over 32 Z regs + // 16 P regs and ffr register to reach end of SVE regs data. + uint32_t fpsr_offset = + z_reg_offset + (z_reg_byte_size * 32) + (vg * 16) + vg; + uint32_t fpcr_offset = fpsr_offset + 4; + + // We iterate over register infos assuming register infos will be indexed + // according to lldb register numbers defined in RegisterInfos_arm64_sve.h + for (auto ® : m_regs) { + if (reg.name[0] == 'v' && isdigit(reg.name[1])) { + reg.byte_offset = v_reg_offset; + v_reg_offset += z_reg_byte_size; + } else if (reg.name[0] == 's' && isdigit(reg.name[1])) { + reg.byte_offset = s_reg_offset; + s_reg_offset += z_reg_byte_size; + } else if (reg.name[0] == 'd' && isdigit(reg.name[1])) { + reg.byte_offset = d_reg_offset; + d_reg_offset += z_reg_byte_size; + } else if (strcmp(reg.name, "fpsr") == 0) { + reg.byte_offset = fpsr_offset; + } else if (strcmp(reg.name, "fpcr") == 0) { + reg.byte_offset = fpcr_offset; + } else if (reg.name[0] == 'z' && isdigit(reg.name[1])) { + reg.byte_size = z_reg_byte_size; + reg.byte_offset = z_reg_offset; + z_reg_offset += z_reg_byte_size; + } else if (reg.name[0] == 'p' && isdigit(reg.name[1])) { + reg.byte_size = vg; + reg.byte_offset = z_reg_offset; + z_reg_offset += vg; + } else if (strcmp(reg.name, "ffr") == 0) { + reg.byte_size = vg; + reg.byte_offset = z_reg_offset; + z_reg_offset += vg; + } + + uint32_t new_end_reg_offset = reg.byte_offset + reg.byte_size; + if (end_reg_offset < new_end_reg_offset) + end_reg_offset = new_end_reg_offset; + } + + return true; + } + + return false; +} + void GDBRemoteDynamicRegisterInfo::CloneFrom( GDBRemoteDynamicRegisterInfoSP proc_reginfo) { m_regs = proc_reginfo->m_regs; Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -38,6 +38,8 @@ #include "llvm/ADT/DenseMap.h" +#include "Utility/ARM64_DWARF_Registers.h" + namespace lldb_private { namespace repro { class Loader; Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1761,6 +1761,27 @@ gdb_thread->PrivateSetRegisterValue(pair.first, buffer_sp->GetData()); } + // Code below is specific to AArch64 target in SVE state + // If expedited register set contains vector granule (vg) register + // then thread's register context reconfiguration is triggered by + // calling UpdateARM64SVERegistersInfos. + const ArchSpec &arch = GetTarget().GetArchitecture(); + if (arch.IsValid() && (arch.GetMachine() == llvm::Triple::aarch64 || + arch.GetMachine() == llvm::Triple::aarch64_be)) { + uint8_t arm64_sve_vg_dwarf_regnum = arm64_dwarf::vg; + GDBRemoteRegisterContext *reg_ctx_sp = + static_cast( + gdb_thread->GetRegisterContext().get()); + + if (reg_ctx_sp) { + uint32_t vg_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( + eRegisterKindDWARF, arm64_sve_vg_dwarf_regnum); + if (expedited_register_map.count(vg_regnum)) { + reg_ctx_sp->AArch64SVEReconfigure(); + } + } + } + thread_sp->SetName(thread_name.empty() ? nullptr : thread_name.c_str()); gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr);