DavidSpickett created this revision.
Herald added subscribers: ctetreau, kristof.beyls.
Herald added a project: All.
DavidSpickett requested review of this revision.
Herald added subscribers: lldb-commits, alextsao1999.
Herald added a project: LLDB.

Software can tell if it is in streaming SVE mode by checking
the Streaming Vector Control Register (SVCR).

"E3.1.9 SVCR, Streaming Vector Control Register" in
"ArmĀ® Architecture Reference Manual Supplement, The Scalable Matrix
Extension (SME), for Armv9-A"

This is especially useful for debug because the names of the
SVE registers are the same betweeen non-streaming and streaming mode.

The Linux Kernel chose to not put this register behind ptrace,
and it can be read from EL0. However, this would mean running code
in process to read it. That can be done but we already know all
the information just from ptrace.

So this is a pseudo register that matches the architectural
content, and it is read only for now until all bits are implemented.
The name is just "svcr", which aligns with GDB's proposed naming.

It is in a new dynamic register set named for the SME exention.
There will also be a streaming vector length pseduo register here
in future.

The register contains two bits:
0 : Whether streaming SVE mode is enabled.
1 : Whether the array storage is enabled.

This patch only adds bit 0, bit 1 will be added in a later patch
after the array storage (ZA) is supported. The register is read
only and may be made writeable in a later patch.

An existing test has been updated to check the expected SVCR value.
There's not much point adding a dedicated test given that the content
is just whatever m_sme_state is. If lldb gets it wrong, svcr will be
wrong too.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D154927

Files:
  lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
  lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
  lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
  lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
  
lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py

Index: lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py
===================================================================
--- lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py
+++ lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py
@@ -22,7 +22,12 @@
             reg_value.GetByteSize(), expected, 'Verify "%s" == %i' % (name, expected)
         )
 
-    def check_sve_regs_read(self, z_reg_size):
+    def check_sve_regs_read(self, z_reg_size, expected_mode):
+        if self.isAArch64SME():
+            expected_value = "1" if expected_mode == Mode.SSVE else "0"
+            self.expect("register read svcr", substrs=[
+                "0x000000000000000" + expected_value])
+
         p_reg_size = int(z_reg_size / 8)
 
         for i in range(32):
@@ -162,7 +167,7 @@
 
         vg_reg_value = sve_registers.GetChildMemberWithName("vg").GetValueAsUnsigned()
         z_reg_size = vg_reg_value * 8
-        self.check_sve_regs_read(z_reg_size)
+        self.check_sve_regs_read(z_reg_size, start_mode)
 
         # Evaluate simple expression and print function expr_eval_func address.
         self.expect("expression expr_eval_func", substrs=["= 0x"])
@@ -174,7 +179,7 @@
 
         # We called a jitted function above which must not have changed SVE
         # vector length or register values.
-        self.check_sve_regs_read(z_reg_size)
+        self.check_sve_regs_read(z_reg_size, start_mode)
 
         self.check_sve_regs_read_after_write(z_reg_size)
 
Index: lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
===================================================================
--- lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
+++ lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
@@ -106,6 +106,8 @@
 
   void AddRegSetTLS();
 
+  void AddRegSetSME();
+
   uint32_t ConfigureVectorLength(uint32_t sve_vq);
 
   bool VectorSizeIsValid(uint32_t vq) {
@@ -127,6 +129,7 @@
   bool IsPAuthReg(unsigned reg) const;
   bool IsMTEReg(unsigned reg) const;
   bool IsTLSReg(unsigned reg) const;
+  bool IsSMEReg(unsigned reg) const;
 
   uint32_t GetRegNumSVEZ0() const;
   uint32_t GetRegNumSVEFFR() const;
@@ -136,6 +139,7 @@
   uint32_t GetPAuthOffset() const;
   uint32_t GetMTEOffset() const;
   uint32_t GetTLSOffset() const;
+  uint32_t GetSMEOffset() const;
 
 private:
   typedef std::map<uint32_t, std::vector<lldb_private::RegisterInfo>>
@@ -163,6 +167,7 @@
   std::vector<uint32_t> pauth_regnum_collection;
   std::vector<uint32_t> m_mte_regnum_collection;
   std::vector<uint32_t> m_tls_regnum_collection;
+  std::vector<uint32_t> m_sme_regnum_collection;
 };
 
 #endif
Index: lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
===================================================================
--- lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
+++ lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
@@ -81,6 +81,9 @@
 static lldb_private::RegisterInfo g_register_infos_tls[] = {
     DEFINE_EXTENSION_REG(tpidr)};
 
+static lldb_private::RegisterInfo g_register_infos_sme[] = {
+    DEFINE_EXTENSION_REG(svcr)};
+
 // Number of register sets provided by this context.
 enum {
   k_num_gpr_registers = gpr_w28 - gpr_x0 + 1,
@@ -89,6 +92,7 @@
   k_num_mte_register = 1,
   k_num_tls_register = 1,
   k_num_pauth_register = 2,
+  k_num_sme_register = 1,
   k_num_register_sets_default = 2,
   k_num_register_sets = 3
 };
@@ -196,6 +200,9 @@
 static const lldb_private::RegisterSet g_reg_set_tls_arm64 = {
     "Thread Local Storage Registers", "tls", k_num_tls_register, nullptr};
 
+static const lldb_private::RegisterSet g_reg_set_sme_arm64 = {
+    "Scalable Matrix Extension Registers", "sme", k_num_sme_register, nullptr};
+
 RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
     const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
     : lldb_private::RegisterInfoAndSetInterface(target_arch),
@@ -240,6 +247,9 @@
       // done as a dynamic set.
       AddRegSetTLS();
 
+      if (m_opt_regsets.AllSet(eRegsetMaskSSVE))
+        AddRegSetSME();
+
       m_register_info_count = m_dynamic_reg_infos.size();
       m_register_info_p = m_dynamic_reg_infos.data();
       m_register_set_p = m_dynamic_reg_sets.data();
@@ -338,6 +348,21 @@
   m_dynamic_reg_sets.back().registers = m_tls_regnum_collection.data();
 }
 
+void RegisterInfoPOSIX_arm64::AddRegSetSME() {
+  uint32_t sme_regnum = m_dynamic_reg_infos.size();
+  m_sme_regnum_collection.push_back(sme_regnum);
+  m_dynamic_reg_infos.push_back(g_register_infos_sme[0]);
+  m_dynamic_reg_infos[sme_regnum].byte_offset =
+      m_dynamic_reg_infos[sme_regnum - 1].byte_offset +
+      m_dynamic_reg_infos[sme_regnum - 1].byte_size;
+  m_dynamic_reg_infos[sme_regnum].kinds[lldb::eRegisterKindLLDB] = sme_regnum;
+
+  m_per_regset_regnum_range[m_register_set_count] =
+      std::make_pair(sme_regnum, sme_regnum + 1);
+  m_dynamic_reg_sets.push_back(g_reg_set_sme_arm64);
+  m_dynamic_reg_sets.back().registers = m_sme_regnum_collection.data();
+}
+
 uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLength(uint32_t sve_vq) {
   // sve_vq contains SVE Quad vector length in context of AArch64 SVE.
   // SVE register infos if enabled cannot be disabled by selecting sve_vq = 0.
@@ -433,6 +458,10 @@
   return llvm::is_contained(m_tls_regnum_collection, reg);
 }
 
+bool RegisterInfoPOSIX_arm64::IsSMEReg(unsigned reg) const {
+  return llvm::is_contained(m_sme_regnum_collection, reg);
+}
+
 uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; }
 
 uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; }
@@ -454,3 +483,7 @@
 uint32_t RegisterInfoPOSIX_arm64::GetTLSOffset() const {
   return m_register_info_p[m_tls_regnum_collection[0]].byte_offset;
 }
+
+uint32_t RegisterInfoPOSIX_arm64::GetSMEOffset() const {
+  return m_register_info_p[m_sme_regnum_collection[0]].byte_offset;
+}
Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -126,6 +126,7 @@
   struct user_pac_mask m_pac_mask;
 
   uint64_t m_mte_ctrl_reg;
+  uint64_t m_sme_ctrl_reg;
 
   uint64_t m_tls_tpidr_reg;
 
@@ -157,10 +158,14 @@
 
   Status WriteTLSTPIDR();
 
+  // SVCR is a pseudo register and we do not allow writes to it.
+  Status ReadSMEControl();
+
   bool IsSVE(unsigned reg) const;
   bool IsPAuth(unsigned reg) const;
   bool IsMTE(unsigned reg) const;
   bool IsTLS(unsigned reg) const;
+  bool IsSME(unsigned reg) const;
 
   uint64_t GetSVERegVG() { return CurrentSVEStateData().m_header.vl / 8; }
 
@@ -172,6 +177,8 @@
 
   void *GetTLSTPIDR() { return &m_tls_tpidr_reg; }
 
+  void *GetSMEControl() { return &m_sme_ctrl_reg; }
+
   void *GetSVEBuffer() { return CurrentSVEStateData().m_buffer.data(); }
 
   size_t GetSVEBufferSize() { return CurrentSVEStateData().m_buffer.size(); }
@@ -186,6 +193,8 @@
 
   size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); }
 
+  size_t GetSMEControlSize() { return sizeof(m_sme_ctrl_reg); }
+
   llvm::Error ReadHardwareDebugInfo() override;
 
   llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
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
@@ -135,6 +135,7 @@
 
   m_mte_ctrl_reg = 0;
   m_tls_tpidr_reg = 0;
+  m_sme_ctrl_reg = 0;
 
   // 16 is just a maximum value, query hardware for actual watchpoint count
   m_max_hwp_supported = 16;
@@ -320,6 +321,13 @@
     offset = reg_info->byte_offset - GetRegisterInfo().GetMTEOffset();
     assert(offset < GetMTEControlSize());
     src = (uint8_t *)GetMTEControl() + offset;
+  } else if (IsSME(reg)) {
+    // This is a pseduo so it never fails.
+    ReadSMEControl();
+
+    offset = reg_info->byte_offset - GetRegisterInfo().GetSMEOffset();
+    assert(offset < GetSMEControlSize());
+    src = (uint8_t *)GetSMEControl() + offset;
   } else
     return Status("failed - register wasn't recognized to be a GPR or an FPR, "
                   "write strategy unknown");
@@ -521,6 +529,8 @@
     ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
 
     return WriteTLSTPIDR();
+  } else if (IsSME(reg)) {
+    return Status("Writing to SVCR is not supported.");
   }
 
   return Status("Failed to write register value");
@@ -734,6 +744,10 @@
   return GetRegisterInfo().IsTLSReg(reg);
 }
 
+bool NativeRegisterContextLinux_arm64::IsSME(unsigned reg) const {
+  return GetRegisterInfo().IsSMEReg(reg);
+}
+
 llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
   if (!m_refresh_hwdebug_info) {
     return llvm::Error::success();
@@ -980,6 +994,16 @@
   return WriteRegisterSet(&ioVec, state.m_buffer.size(), state.m_regset);
 }
 
+Status NativeRegisterContextLinux_arm64::ReadSMEControl() {
+  // The real register is SVCR and is accessible from EL0. However we don't want
+  // to have to JIT code into the target process so we'll just recreate it using
+  // what we know from ptrace.
+  // Bit 1 indicates whether streaming mode is active.
+  // Bit 2 indicates whether the array storage is active (not yet implemented).
+  m_sme_ctrl_reg = m_sve_state == SVEState::Streaming;
+  return {};
+}
+
 Status NativeRegisterContextLinux_arm64::ReadMTEControl() {
   Status error;
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to