[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:
Muhammad Omair Javaid 2021-01-19 14:52:23 +05:00
parent 079e664661
commit e448ad787e
6 changed files with 126 additions and 11 deletions

View file

@ -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

View file

@ -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 &reg : m_regs) {
if (strcmp(reg.name, "vg") == 0) {
m_is_reconfigurable = true;
break;
}
}
}
}
void DynamicRegisterInfo::ConfigureOffsets() {

View file

@ -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();

View file

@ -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 &reg : 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)

View file

@ -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;

View file

@ -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);