[Lldb-commits] [lldb] d7fab66 - [lldb][AArch64] Add SME's streaming vector control register
David Spickett via lldb-commits
lldb-commits at lists.llvm.org
Wed Sep 20 02:20:51 PDT 2023
Author: David Spickett
Date: 2023-09-20T10:20:45+01:00
New Revision: d7fab666955e9ef0d456b6dfb737f5a18241c97c
URL: https://github.com/llvm/llvm-project/commit/d7fab666955e9ef0d456b6dfb737f5a18241c97c
DIFF: https://github.com/llvm/llvm-project/commit/d7fab666955e9ef0d456b6dfb737f5a18241c97c.diff
LOG: [lldb][AArch64] Add SME's streaming vector control register
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"
https://developer.arm.com/documentation/ddi0616/latest/
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. The name is just "svcr", which aligns with GDB's proposed naming,
and it's added to the existing SME register set.
The SVCR register contains two bits:
0 : Whether streaming SVE mode is enabled (SM)
1 : Whether the array storage is enabled (ZA)
Array storage can be active when streaming mode is not, so this register
can have any permutation of those bits.
This register is currently read only. We can emulate the result of
writing to it, using ptrace. However at this point the utility of that
is not obvious.
Existing tests have been updated to check for appropriate SVCR values
at various points.
Given that this register is a read only pseudo, there is no need
to save and restore it around expressions.
Reviewed By: omjavaid
Differential Revision: https://reviews.llvm.org/D154927
Added:
Modified:
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/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.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/main.c
lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py
Removed:
################################################################################
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
index 7d246eeb5fc9b7e..b5210c368144206 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -360,6 +360,9 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
if (error.Fail())
return error;
+ // This is a psuedo so it never fails.
+ ReadSMEControl();
+
offset = reg_info->byte_offset - GetRegisterInfo().GetSMEOffset();
assert(offset < GetSMEPseudoBufferSize());
src = (uint8_t *)GetSMEPseudoBuffer() + offset;
@@ -550,7 +553,7 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
return WriteTLS();
} else if (IsSME(reg)) {
if (!GetRegisterInfo().IsSMERegZA(reg))
- return Status("Writing to SVG is not supported.");
+ return Status("Writing to SVG or SVCR is not supported.");
error = ReadZA();
if (error.Fail())
@@ -1160,6 +1163,24 @@ Status NativeRegisterContextLinux_arm64::WriteAllSVE() {
return WriteRegisterSet(&ioVec, GetSVEBufferSize(), GetSVERegSet());
}
+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 0 indicates whether streaming mode is active.
+ m_sme_pseudo_regs.ctrl_reg = m_sve_state == SVEState::Streaming;
+
+ // Bit 1 indicates whether the array storage is active.
+ // It is active if we can read the header and the size field tells us that
+ // there is register data following it.
+ Status error = ReadZAHeader();
+ if (error.Success() && (m_za_header.size > sizeof(m_za_header)))
+ m_sme_pseudo_regs.ctrl_reg |= 2;
+
+ return error;
+}
+
Status NativeRegisterContextLinux_arm64::ReadMTEControl() {
Status error;
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
index 20e262fcd0f3dc9..ed2653485c3e7da 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -115,6 +115,7 @@ class NativeRegisterContextLinux_arm64
uint64_t m_mte_ctrl_reg;
struct sme_pseudo_regs {
+ uint64_t ctrl_reg;
uint64_t svg_reg;
};
@@ -162,6 +163,9 @@ class NativeRegisterContextLinux_arm64
// Instead use WriteZA and ensure you have the correct ZA buffer size set
// beforehand if you wish to disable it.
+ // SVCR is a pseudo register and we do not allow writes to it.
+ Status ReadSMEControl();
+
bool IsSVE(unsigned reg) const;
bool IsSME(unsigned reg) const;
bool IsPAuth(unsigned reg) const;
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
index 8b23f17ca573fff..60070819cb92699 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
@@ -84,6 +84,7 @@ static lldb_private::RegisterInfo g_register_infos_tls[] = {
DEFINE_EXTENSION_REG(tpidr2)};
static lldb_private::RegisterInfo g_register_infos_sme[] = {
+ DEFINE_EXTENSION_REG(svcr),
DEFINE_EXTENSION_REG(svg),
// 16 is a default size we will change later.
{"za", nullptr, 16, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8,
@@ -97,7 +98,7 @@ enum {
k_num_mte_register = 1,
// Number of TLS registers is dynamic so it is not listed here.
k_num_pauth_register = 2,
- k_num_sme_register = 2,
+ k_num_sme_register = 3,
k_num_register_sets_default = 2,
k_num_register_sets = 3
};
@@ -449,7 +450,7 @@ void RegisterInfoPOSIX_arm64::ConfigureVectorLengthZA(uint32_t za_vq) {
// dynamic set and is just 1 register so we make an exception to const here.
lldb_private::RegisterInfo *non_const_reginfo =
const_cast<lldb_private::RegisterInfo *>(m_register_info_p);
- non_const_reginfo[m_sme_regnum_collection[1]].byte_size =
+ non_const_reginfo[m_sme_regnum_collection[2]].byte_size =
(za_vq * 16) * (za_vq * 16);
}
@@ -473,7 +474,7 @@ bool RegisterInfoPOSIX_arm64::IsSVERegVG(unsigned reg) const {
}
bool RegisterInfoPOSIX_arm64::IsSMERegZA(unsigned reg) const {
- return reg == m_sme_regnum_collection[1];
+ return reg == m_sme_regnum_collection[2];
}
bool RegisterInfoPOSIX_arm64::IsPAuthReg(unsigned reg) const {
@@ -503,7 +504,7 @@ uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPSR() const { return fpu_fpsr; }
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEVG() const { return sve_vg; }
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSMESVG() const {
- return m_sme_regnum_collection[0];
+ return m_sme_regnum_collection[1];
}
uint32_t RegisterInfoPOSIX_arm64::GetPAuthOffset() const {
diff --git a/lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py b/lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py
index 4f4da2b5223fb15..2fb8b33126417c2 100644
--- a/lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py
+++ b/lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py
@@ -172,6 +172,13 @@ def test_aarch64_dynamic_regset_config_sme(self):
svg = sme_registers.GetChildMemberWithName("svg").GetValueAsUnsigned()
self.assertEqual(vg, svg)
+ # SVCR should be SVCR.SM | SVCR.ZA aka 3 because streaming mode is on
+ # and ZA is enabled.
+ svcr = sme_registers.GetChildMemberWithName("svcr").GetValueAsUnsigned()
+ self.assertEqual(3, svcr)
+
+ # SVCR is read only so we do not test writing to it.
+
@no_debug_info_test
@skipIf(archs=no_match(["aarch64"]))
@skipIf(oslist=no_match(["linux"]))
@@ -192,6 +199,10 @@ def test_aarch64_dynamic_regset_config_sme_za_disabled(self):
self.assertTrue(sme_registers.IsValid())
svg = sme_registers.GetChildMemberWithName("svg").GetValueAsUnsigned()
+ # We are not in streaming mode, ZA is disabled, so this should be 0.
+ svcr = sme_registers.GetChildMemberWithName("svcr").GetValueAsUnsigned()
+ self.assertEqual(0, svcr)
+
svl = svg * 8
# A disabled ZA is shown as all 0s.
self.expect("register read za", substrs=[self.make_za_value(svl, lambda r: 0)])
@@ -200,3 +211,8 @@ def test_aarch64_dynamic_regset_config_sme_za_disabled(self):
# it back.
self.runCmd("register write za '{}'".format(za_value))
self.expect("register read za", substrs=[za_value])
+
+ # Now SVCR.ZA should be set, which is bit 1.
+ self.expect("register read svcr", substrs=["0x0000000000000002"])
+
+ # SVCR is read only so we do not test writing to it.
diff --git a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py
index 0fd18977b28b0b8..82b79b8d4b6cc2b 100644
--- a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py
+++ b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py
@@ -24,7 +24,15 @@ def check_sve_register_size(self, set, name, expected):
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():
+ # This test uses SMSTART SM, which only enables streaming mode,
+ # leaving ZA disabled.
+ 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):
@@ -168,7 +176,7 @@ def sve_registers_read_write_impl(self, start_mode, eval_mode):
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"])
@@ -184,7 +192,7 @@ def sve_registers_read_write_impl(self, start_mode, eval_mode):
# 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)
diff --git a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c
index b07fb62ed85b74c..1e654ad26b1cef4 100644
--- a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c
+++ b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c
@@ -9,7 +9,7 @@
#define PR_SME_SET_VL 63
#endif
-#define SMSTART() asm volatile("msr s0_3_c4_c7_3, xzr" /*smstart*/)
+#define SMSTART_SM() asm volatile("msr s0_3_c4_c3_3, xzr" /*smstart sm*/)
void write_sve_regs() {
// We assume the smefa64 feature is present, which allows ffr access
@@ -130,18 +130,18 @@ int expr_eval_func(bool streaming) {
// Note that doing a syscall brings you back to non-streaming mode, so we
// don't need to SMSTOP here.
if (streaming)
- SMSTART();
+ SMSTART_SM();
write_sve_regs_expr();
prctl(SET_VL_OPT, 8 * 4);
if (streaming)
- SMSTART();
+ SMSTART_SM();
write_sve_regs_expr();
return 1;
}
int main() {
#ifdef START_SSVE
- SMSTART();
+ SMSTART_SM();
#endif
write_sve_regs();
diff --git a/lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py b/lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py
index 1d4bbd6207a51c1..910966a0b3b0bc5 100644
--- a/lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py
+++ b/lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py
@@ -164,6 +164,10 @@ def za_expr_test_impl(self, sve_mode, za_state, swap_start_vl):
self.runCmd("register read " + sve_reg_names)
sve_values = self.res.GetOutput()
+ svcr_value = 1 if sve_mode == Mode.SSVE else 0
+ if za_state == ZA.Enabled:
+ svcr_value += 2
+
def check_regs():
if za_state == ZA.Enabled:
self.check_za(start_vl)
@@ -175,6 +179,7 @@ def check_regs():
self.assertEqual(start_vg, self.read_vg())
self.expect("register read " + sve_reg_names, substrs=[sve_values])
+ self.expect("register read svcr", substrs=["0x{:016x}".format(svcr_value)])
for expr in exprs:
expr_cmd = "expression {}()".format(expr)
More information about the lldb-commits
mailing list