OSDN Git Service

[LLDB] Add support to resize SVE registers at run-time
authorMuhammad Omair Javaid <omair.javaid@linaro.org>
Tue, 19 Jan 2021 09:52:23 +0000 (14:52 +0500)
committerMuhammad Omair Javaid <omair.javaid@linaro.org>
Tue, 19 Jan 2021 10:01:32 +0000 (15:01 +0500)
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

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp
lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

index 6b2dd25..c34afe6 100644 (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
index 8b85c78..5463a07 100644 (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() {
index 31d040e..fbf9db6 100644 (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();
index 19bcac5..1000661 100644 (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)
index 475e65a..252d7b3 100644 (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;
 
index f9b0632..89a8597 100644 (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);