[LLDB] Add support to resize SVE registers at run-time
This patch builds on previously submitted SVE patches regarding expedited register set and per thread register infos. (D82853 D82855 and D82857) We need to resize SVE register based on value received in expedited list. Also we need to resize SVE registers when we write vg register using register write vg command. The resize will result in a updated offset for all of fpr and sve register set. This offset will be configured in native register context by RegisterInfoInterface and will also be be updated on client side in GDBRemoteRegisterContext. A follow up patch will provide a API test to verify this change. Reviewed By: labath Differential Revision: https://reviews.llvm.org/D82863
This commit is contained in:
parent
079e664661
commit
e448ad787e
|
@ -299,14 +299,31 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
|
|||
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
|
||||
|
|
|
@ -617,6 +617,17 @@ void DynamicRegisterInfo::Finalize(const ArchSpec &arch) {
|
|||
// 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() {
|
||||
|
|
|
@ -66,6 +66,9 @@ public:
|
|||
|
||||
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<lldb_private::RegisterInfo> reg_collection;
|
||||
|
@ -77,9 +80,6 @@ protected:
|
|||
typedef std::vector<uint8_t> dwarf_opcode;
|
||||
typedef std::map<uint32_t, dwarf_opcode> dynamic_reg_size_map;
|
||||
|
||||
const lldb_private::RegisterInfo *
|
||||
GetRegisterInfo(llvm::StringRef reg_name) const;
|
||||
|
||||
void MoveFrom(DynamicRegisterInfo &&info);
|
||||
|
||||
void ConfigureOffsets();
|
||||
|
|
|
@ -214,8 +214,8 @@ bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info,
|
|||
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 @@ bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info,
|
|||
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 @@ bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info,
|
|||
|
||||
{
|
||||
SetAllRegisterValid(false);
|
||||
|
||||
if (do_reconfigure_arm64_sve)
|
||||
AArch64SVEReconfigure();
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
@ -391,6 +404,9 @@ bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info,
|
|||
} 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 @@ bool GDBRemoteRegisterContext::WriteAllRegisterValues(
|
|||
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 @@ uint32_t GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber(
|
|||
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<DataBufferHeap>(
|
||||
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)
|
||||
|
|
|
@ -39,6 +39,7 @@ public:
|
|||
~GDBRemoteDynamicRegisterInfo() override = default;
|
||||
|
||||
void HardcodeARMRegisters(bool from_scratch);
|
||||
bool UpdateARM64SVERegistersInfos(uint64_t vg);
|
||||
};
|
||||
|
||||
class GDBRemoteRegisterContext : public RegisterContext {
|
||||
|
@ -77,6 +78,8 @@ public:
|
|||
uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
|
||||
uint32_t num) override;
|
||||
|
||||
bool AArch64SVEReconfigure();
|
||||
|
||||
protected:
|
||||
friend class ThreadGDBRemote;
|
||||
|
||||
|
|
|
@ -1763,6 +1763,19 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
|
|||
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<GDBRemoteRegisterContext *>(
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue