https://github.com/ayushsahay1837 updated https://github.com/llvm/llvm-project/pull/205906
>From b646150d1046282382aa2c6400d79353472bbfe5 Mon Sep 17 00:00:00 2001 From: Ayush Sahay <[email protected]> Date: Fri, 19 Jun 2026 11:25:05 +0530 Subject: [PATCH 1/4] [lldb][Windows] Add SVE register read/write support in lldb-server Enable reading and writing of SVE registers in non-streaming mode for lldb-server. Non-streaming SVE support was added in Windows SDK 10.0.26100 (Windows 11 24H2). Microsoft has not yet published a Windows SDK with support for streaming SVE. So, streaming mode register access remains unsupported. Detect SVE availability and configure the appropriate register sets accordingly. The lower 128 bits of Z registers are mapped to NEON V registers in the CONTEXT struct, while the higher bits reside in a packed array within the XState SVE feature area. Put together the whole Z register by concatenating the low bits with the high bits. The predicate registers reside in a packed array within the XState SVE feature area too, with the First Fault Register located immediately after them. On Windows, SVE vector length is fixed for the process lifetime and cannot be changed dynamically, unlike Linux. Currently, the default non- streaming SVE vector length is 128 bits. So, the interleaving of the low and the high bits of Z registers remains untested. Assisted-by: Claude Sonnet 4.6 --- .../Python/lldbsuite/test/cpu_feature.py | 40 +- .../Utility/RegisterInfoPOSIX_arm64.cpp | 2 + .../Process/Utility/RegisterInfoPOSIX_arm64.h | 1 + .../NativeRegisterContextWindows_arm64.cpp | 620 +++++++++++++++--- .../NativeRegisterContextWindows_arm64.h | 39 +- .../aarch64_sve_registers_windows/Makefile | 5 + .../TestSVERegisters.py | 170 +++++ .../aarch64_sve_registers_windows/main.c | 152 +++++ 8 files changed, 924 insertions(+), 105 deletions(-) create mode 100644 lldb/test/API/commands/register/aarch64_sve_registers_windows/Makefile create mode 100644 lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegisters.py create mode 100644 lldb/test/API/commands/register/aarch64_sve_registers_windows/main.c diff --git a/lldb/packages/Python/lldbsuite/test/cpu_feature.py b/lldb/packages/Python/lldbsuite/test/cpu_feature.py index 736d5e86399ae..6baa7d9615e02 100644 --- a/lldb/packages/Python/lldbsuite/test/cpu_feature.py +++ b/lldb/packages/Python/lldbsuite/test/cpu_feature.py @@ -5,10 +5,13 @@ import re +PF_ARM_SVE_INSTRUCTIONS_AVAILABLE = 46 + class CPUFeature: - def __init__(self, linux_cpu_info_flag: str = None, darwin_sysctl_key: str = None): + def __init__(self, linux_cpu_info_flag: str = None, darwin_sysctl_key: str = None, windows_processor_feature: int = None): self.cpu_info_flag = linux_cpu_info_flag self.sysctl_key = darwin_sysctl_key + self.windows_processor_feature = windows_processor_feature def __str__(self): for arch_class in ALL_ARCHS: @@ -22,6 +25,8 @@ def is_supported(self, triple, cmd_runner): err_msg, res = self._is_supported_linux(cmd_runner) elif re.match(".*-apple-.*", triple): err_msg, res = self._is_supported_darwin(cmd_runner) + elif re.match(".*-windows-.*", triple): + err_msg, res = self._is_supported_windows(cmd_runner) else: err_msg, res = None, False @@ -58,6 +63,37 @@ def _is_supported_darwin(self, cmd_runner): return None, (output.strip() == "1") + # PowerShell may not be on PATH on minimal Windows images, and Add-Type + # requires the .NET CLR and the CSC compiler to be available. Neither is + # guaranteed. + # TODO: Replace the PowerShell chain with a probe that calls + # 'IsProcessorFeaturePresent' directly. + def _is_supported_windows(self, cmd_runner): + import base64 + + if self.windows_processor_feature is None: + return f"Unspecified processor feature ID for {self}", False + + # IsProcessorFeaturePresent() via PowerShell + ps_script = ( + "Add-Type -TypeDefinition '" + "using System; using System.Runtime.InteropServices; " + "public class WinAPI { " + "[DllImport(\"kernel32.dll\")] " + "public static extern bool IsProcessorFeaturePresent(uint f); }'; " + f"[WinAPI]::IsProcessorFeaturePresent({self.windows_processor_feature})" + ) + + # PowerShell -EncodedCommand expects UTF-16LE Base64. + encoded = base64.b64encode(ps_script.encode("utf-16-le")).decode("ascii") + cmd = f"powershell -EncodedCommand {encoded}" + err, retcode, output = cmd_runner(cmd) + if err.Fail() or retcode != 0: + return ("Windows SVE detection via PowerShell failed " + "(retcode={0}, output={1!r})".format(retcode, output)), False + + return None, (output.strip().lower() == "true") + class AArch64: FPMR = CPUFeature("fpmr") @@ -69,7 +105,7 @@ class AArch64: SME = CPUFeature("sme", "hw.optional.arm.FEAT_SME") SME_FA64 = CPUFeature("smefa64") SME2 = CPUFeature("sme2", "hw.optional.arm.FEAT_SME2") - SVE = CPUFeature("sve") + SVE = CPUFeature("sve", windows_processor_feature=PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) class Loong: diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp index 0a6424eec0096..15b2bf0ddd39c 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp @@ -638,6 +638,8 @@ bool RegisterInfoPOSIX_arm64::IsPOEReg(unsigned reg) const { uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; } +uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEP0() const { return sve_p0; } + uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; } uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPCR() const { return fpu_fpcr; } diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h index 378ea3f524f6c..7d5e9222b60d4 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h @@ -174,6 +174,7 @@ class RegisterInfoPOSIX_arm64 bool IsPOEReg(unsigned reg) const; uint32_t GetRegNumSVEZ0() const; + uint32_t GetRegNumSVEP0() const; uint32_t GetRegNumSVEFFR() const; uint32_t GetRegNumFPCR() const; uint32_t GetRegNumFPSR() const; diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp index 8b1d7276d00bd..0e2ee4d2f93cd 100644 --- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp @@ -20,87 +20,11 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" +#include <optional> + using namespace lldb; using namespace lldb_private; -#define REG_CONTEXT_SIZE sizeof(::CONTEXT) - -namespace { -static const uint32_t g_gpr_regnums_arm64[] = { - gpr_x0_arm64, gpr_x1_arm64, gpr_x2_arm64, gpr_x3_arm64, - gpr_x4_arm64, gpr_x5_arm64, gpr_x6_arm64, gpr_x7_arm64, - gpr_x8_arm64, gpr_x9_arm64, gpr_x10_arm64, gpr_x11_arm64, - gpr_x12_arm64, gpr_x13_arm64, gpr_x14_arm64, gpr_x15_arm64, - gpr_x16_arm64, gpr_x17_arm64, gpr_x18_arm64, gpr_x19_arm64, - gpr_x20_arm64, gpr_x21_arm64, gpr_x22_arm64, gpr_x23_arm64, - gpr_x24_arm64, gpr_x25_arm64, gpr_x26_arm64, gpr_x27_arm64, - gpr_x28_arm64, gpr_fp_arm64, gpr_lr_arm64, gpr_sp_arm64, - gpr_pc_arm64, gpr_cpsr_arm64, gpr_w0_arm64, gpr_w1_arm64, - gpr_w2_arm64, gpr_w3_arm64, gpr_w4_arm64, gpr_w5_arm64, - gpr_w6_arm64, gpr_w7_arm64, gpr_w8_arm64, gpr_w9_arm64, - gpr_w10_arm64, gpr_w11_arm64, gpr_w12_arm64, gpr_w13_arm64, - gpr_w14_arm64, gpr_w15_arm64, gpr_w16_arm64, gpr_w17_arm64, - gpr_w18_arm64, gpr_w19_arm64, gpr_w20_arm64, gpr_w21_arm64, - gpr_w22_arm64, gpr_w23_arm64, gpr_w24_arm64, gpr_w25_arm64, - gpr_w26_arm64, gpr_w27_arm64, gpr_w28_arm64, - LLDB_INVALID_REGNUM // Register set must be terminated with this flag -}; -static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) - - 1) == k_num_gpr_registers_arm64, - "g_gpr_regnums_arm64 has wrong number of register infos"); - -static const uint32_t g_fpr_regnums_arm64[] = { - fpu_v0_arm64, fpu_v1_arm64, fpu_v2_arm64, fpu_v3_arm64, - fpu_v4_arm64, fpu_v5_arm64, fpu_v6_arm64, fpu_v7_arm64, - fpu_v8_arm64, fpu_v9_arm64, fpu_v10_arm64, fpu_v11_arm64, - fpu_v12_arm64, fpu_v13_arm64, fpu_v14_arm64, fpu_v15_arm64, - fpu_v16_arm64, fpu_v17_arm64, fpu_v18_arm64, fpu_v19_arm64, - fpu_v20_arm64, fpu_v21_arm64, fpu_v22_arm64, fpu_v23_arm64, - fpu_v24_arm64, fpu_v25_arm64, fpu_v26_arm64, fpu_v27_arm64, - fpu_v28_arm64, fpu_v29_arm64, fpu_v30_arm64, fpu_v31_arm64, - fpu_s0_arm64, fpu_s1_arm64, fpu_s2_arm64, fpu_s3_arm64, - fpu_s4_arm64, fpu_s5_arm64, fpu_s6_arm64, fpu_s7_arm64, - fpu_s8_arm64, fpu_s9_arm64, fpu_s10_arm64, fpu_s11_arm64, - fpu_s12_arm64, fpu_s13_arm64, fpu_s14_arm64, fpu_s15_arm64, - fpu_s16_arm64, fpu_s17_arm64, fpu_s18_arm64, fpu_s19_arm64, - fpu_s20_arm64, fpu_s21_arm64, fpu_s22_arm64, fpu_s23_arm64, - fpu_s24_arm64, fpu_s25_arm64, fpu_s26_arm64, fpu_s27_arm64, - fpu_s28_arm64, fpu_s29_arm64, fpu_s30_arm64, fpu_s31_arm64, - - fpu_d0_arm64, fpu_d1_arm64, fpu_d2_arm64, fpu_d3_arm64, - fpu_d4_arm64, fpu_d5_arm64, fpu_d6_arm64, fpu_d7_arm64, - fpu_d8_arm64, fpu_d9_arm64, fpu_d10_arm64, fpu_d11_arm64, - fpu_d12_arm64, fpu_d13_arm64, fpu_d14_arm64, fpu_d15_arm64, - fpu_d16_arm64, fpu_d17_arm64, fpu_d18_arm64, fpu_d19_arm64, - fpu_d20_arm64, fpu_d21_arm64, fpu_d22_arm64, fpu_d23_arm64, - fpu_d24_arm64, fpu_d25_arm64, fpu_d26_arm64, fpu_d27_arm64, - fpu_d28_arm64, fpu_d29_arm64, fpu_d30_arm64, fpu_d31_arm64, - fpu_fpsr_arm64, fpu_fpcr_arm64, - LLDB_INVALID_REGNUM // Register set must be terminated with this flag -}; -static_assert(((sizeof g_fpr_regnums_arm64 / sizeof g_fpr_regnums_arm64[0]) - - 1) == k_num_fpr_registers_arm64, - "g_fpu_regnums_arm64 has wrong number of register infos"); - -static const RegisterSet g_reg_sets_arm64[] = { - {"General Purpose Registers", "gpr", std::size(g_gpr_regnums_arm64) - 1, - g_gpr_regnums_arm64}, - {"Floating Point Registers", "fpr", std::size(g_fpr_regnums_arm64) - 1, - g_fpr_regnums_arm64}, -}; - -enum { k_num_register_sets = 2 }; - -} // namespace - -static RegisterInfoInterface * -CreateRegisterInfoInterface(const ArchSpec &target_arch) { - assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && - "Register setting path assumes this is a 64-bit host"); - return new RegisterInfoPOSIX_arm64( - target_arch, RegisterInfoPOSIX_arm64::eRegsetMaskDefault); -} - static Status GetThreadContextLength(DWORD context_flags, DWORD &context_length) { Log *log = GetLog(WindowsLog::Registers); @@ -124,9 +48,14 @@ static Status GetThreadContextLength(DWORD context_flags, return error; } -static Status GetThreadContextHelper(lldb::thread_t thread_handle, - DWORD context_flags, PCONTEXT &context, - DataBufferHeap *context_buffer) { +static Status GetThreadContextHelper( + lldb::thread_t thread_handle, DWORD context_flags, PCONTEXT &context, + DataBufferHeap *context_buffer +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + , + std::optional<DWORD64> xstate_features_mask = std::nullopt +#endif +) { Log *log = GetLog(WindowsLog::Registers); Status error; DWORD context_length = 0; @@ -154,6 +83,16 @@ static Status GetThreadContextHelper(lldb::thread_t thread_handle, return error; } +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + if (xstate_features_mask.has_value()) { + if (!SetXStateFeaturesMask(context, *xstate_features_mask)) { + error = Status(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "SetXStateFeaturesMask failed with error {0}", error); + return error; + } + } +#endif + if (!::GetThreadContext(thread_handle, context)) { error = Status(GetLastError(), eErrorTypeWin32); LLDB_LOG(log, "GetThreadContext failed with error {0}", error); @@ -179,38 +118,67 @@ std::unique_ptr<NativeRegisterContextWindows> NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { // Register context for a native 64-bit application. - return std::make_unique<NativeRegisterContextWindows_arm64>(target_arch, - native_thread); + + // Configure register sets supported by this AArch64 target. + Flags opt_regsets; + +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE)) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE); +#endif + + auto register_info_up = + std::make_unique<RegisterInfoPOSIX_arm64>(target_arch, opt_regsets); + + return std::make_unique<NativeRegisterContextWindows_arm64>( + target_arch, native_thread, std::move(register_info_up)); } NativeRegisterContextWindows_arm64::NativeRegisterContextWindows_arm64( - const ArchSpec &target_arch, NativeThreadProtocol &native_thread) - : NativeRegisterContextRegisterInfo( - native_thread, CreateRegisterInfoInterface(target_arch)), - m_context(nullptr), m_context_buffer(nullptr) { + const ArchSpec &target_arch, NativeThreadProtocol &native_thread, + std::unique_ptr<RegisterInfoPOSIX_arm64> register_info_up) + : NativeRegisterContextRegisterInfo(native_thread, + register_info_up.release()), + m_context(nullptr), m_context_buffer(nullptr) +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + , + m_sve_header(nullptr), m_sve_header_is_valid(false), + m_sve_z_buffer(nullptr), m_sve_z_buffer_is_valid(false) +#endif +{ + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + // Currently, there is no API to query the maximum supported hardware // breakpoints and watchpoints on Windows. The values set below are based // on tests conducted on Windows 11 with Snapdragon Elite X hardware. m_max_hwp_supported = 1; + +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + if (GetRegisterInfo().IsSVEPresent()) + m_sve_state = SVEState::Unknown; + else + m_sve_state = SVEState::Disabled; +#endif } bool NativeRegisterContextWindows_arm64::IsGPR(uint32_t reg_index) const { - return (reg_index >= k_first_gpr_arm64 && reg_index <= k_last_gpr_arm64); + return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg_index) == + RegisterInfoPOSIX_arm64::GPRegSet; } bool NativeRegisterContextWindows_arm64::IsFPR(uint32_t reg_index) const { - return (reg_index >= k_first_fpr_arm64 && reg_index <= k_last_fpr_arm64); + return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg_index) == + RegisterInfoPOSIX_arm64::FPRegSet; } uint32_t NativeRegisterContextWindows_arm64::GetRegisterSetCount() const { - return k_num_register_sets; + return GetRegisterInfo().GetRegisterSetCount(); } const RegisterSet * NativeRegisterContextWindows_arm64::GetRegisterSet(uint32_t set_index) const { - if (set_index >= k_num_register_sets) - return nullptr; - return &g_reg_sets_arm64[set_index]; + return GetRegisterInfo().GetRegisterSet(set_index); } Status NativeRegisterContextWindows_arm64::GPRRead(const uint32_t reg, @@ -308,7 +276,14 @@ Status NativeRegisterContextWindows_arm64::GPRRead(const uint32_t reg, Status NativeRegisterContextWindows_arm64::GPRWrite(const uint32_t reg, const RegisterValue ®_value) { - auto cleanup = llvm::make_scope_exit([&]() { m_context = nullptr; }); + auto cleanup = llvm::make_scope_exit([&]() { + m_context = nullptr; +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + m_sve_header = nullptr; + m_sve_header_is_valid = false; + m_sve_z_buffer_is_valid = false; +#endif + }); PCONTEXT context = nullptr; DataBufferHeap context_buffer; @@ -533,7 +508,14 @@ Status NativeRegisterContextWindows_arm64::FPRRead(const uint32_t reg, Status NativeRegisterContextWindows_arm64::FPRWrite(const uint32_t reg, const RegisterValue ®_value) { - auto cleanup = llvm::make_scope_exit([&]() { m_context = nullptr; }); + auto cleanup = llvm::make_scope_exit([&]() { + m_context = nullptr; +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + m_sve_header = nullptr; + m_sve_header_is_valid = false; + m_sve_z_buffer_is_valid = false; +#endif + }); PCONTEXT context = nullptr; DataBufferHeap context_buffer; @@ -689,6 +671,11 @@ NativeRegisterContextWindows_arm64::ReadRegister(const RegisterInfo *reg_info, if (IsFPR(reg)) return FPRRead(reg, reg_value); +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + if (IsSVE(reg)) + return SVERead(reg, reg_value); +#endif + return Status::FromErrorString("unimplemented"); } @@ -718,6 +705,11 @@ Status NativeRegisterContextWindows_arm64::WriteRegister( if (IsFPR(reg)) return FPRWrite(reg, reg_value); +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + if (IsSVE(reg)) + return SVEWrite(reg, reg_value); +#endif + return Status::FromErrorString("unimplemented"); } @@ -743,7 +735,14 @@ Status NativeRegisterContextWindows_arm64::ReadAllRegisterValues( Status NativeRegisterContextWindows_arm64::WriteAllRegisterValues( const lldb::DataBufferSP &data_sp) { - auto cleanup = llvm::make_scope_exit([&]() { m_context = nullptr; }); + auto cleanup = llvm::make_scope_exit([&]() { + m_context = nullptr; +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + m_sve_header = nullptr; + m_sve_header_is_valid = false; + m_sve_z_buffer_is_valid = false; +#endif + }); Log *log = GetLog(WindowsLog::Registers); Status error; @@ -755,6 +754,16 @@ Status NativeRegisterContextWindows_arm64::WriteAllRegisterValues( } DWORD context_flags = CONTEXT_ALL; + +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + std::optional<DWORD64> xstate_features_mask = std::nullopt; + + if (GetRegisterInfo().IsSVEPresent()) { + context_flags |= CONTEXT_XSTATE; + xstate_features_mask = XSTATE_MASK_ARM64_SVE; + } +#endif + DWORD context_length = 0; error = GetThreadContextLength(context_flags, context_length); @@ -772,7 +781,12 @@ Status NativeRegisterContextWindows_arm64::WriteAllRegisterValues( PCONTEXT context = nullptr; DataBufferHeap context_buffer; error = GetThreadContextHelper(GetThreadHandle(), context_flags, context, - &context_buffer); + &context_buffer +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + , + xstate_features_mask +#endif + ); if (error.Fail()) return error; @@ -796,7 +810,14 @@ llvm::Error NativeRegisterContextWindows_arm64::ReadHardwareDebugInfo() { llvm::Error NativeRegisterContextWindows_arm64::WriteHardwareDebugRegs(DREGType hwbType) { - auto cleanup = llvm::make_scope_exit([&]() { m_context = nullptr; }); + auto cleanup = llvm::make_scope_exit([&]() { + m_context = nullptr; +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + m_sve_header = nullptr; + m_sve_header_is_valid = false; + m_sve_z_buffer_is_valid = false; +#endif + }); PCONTEXT context = nullptr; DataBufferHeap context_buffer; @@ -821,27 +842,424 @@ NativeRegisterContextWindows_arm64::WriteHardwareDebugRegs(DREGType hwbType) { void NativeRegisterContextWindows_arm64::InvalidateAllRegisters() { m_context = nullptr; m_context_buffer.reset(); +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + m_sve_header = nullptr; + m_sve_header_is_valid = false; + m_sve_z_buffer.reset(); + m_sve_z_buffer_is_valid = false; + + // Update SVE registers in case there is any change in the configuration. + ConfigureRegisterContext(); +#endif } Status NativeRegisterContextWindows_arm64::CacheAllRegisterValues() { Status error; DWORD context_flags = CONTEXT_ALL; +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + std::optional<DWORD64> xstate_features_mask = std::nullopt; + const bool sve_present = GetRegisterInfo().IsSVEPresent(); + + if (sve_present) { + context_flags |= CONTEXT_XSTATE; + xstate_features_mask = XSTATE_MASK_ARM64_SVE; + } +#endif + if (m_context && (m_context->ContextFlags & context_flags) == context_flags) - return error; +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + { + if (!sve_present) +#endif + return error; + +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + if (m_sve_header_is_valid && m_sve_z_buffer_is_valid) + return error; + } + m_sve_header = nullptr; + m_sve_header_is_valid = false; + m_sve_z_buffer_is_valid = false; +#endif m_context = nullptr; + auto cleanup = llvm::make_scope_exit([&]() { + if (error.Fail()) { + m_context = nullptr; +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + m_sve_header = nullptr; + m_sve_header_is_valid = false; + m_sve_z_buffer_is_valid = false; + if (sve_present) + m_sve_state = SVEState::Unknown; + else + m_sve_state = SVEState::Disabled; +#endif + } + }); + if (!m_context_buffer) m_context_buffer = std::make_shared<DataBufferHeap>(); error = GetThreadContextHelper(GetThreadHandle(), context_flags, m_context, - m_context_buffer.get()); + m_context_buffer.get() +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + , + xstate_features_mask +#endif + ); + if (error.Fail()) + return error; + +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + if (sve_present) { + error = ReadSVEHeader(); + if (error.Fail()) + return error; + + error = CacheSVEZRegisters(); + if (error.Fail()) + return error; + } else { + m_sve_state = SVEState::Disabled; + } +#endif + + return error; +} + +RegisterInfoPOSIX_arm64 & +NativeRegisterContextWindows_arm64::GetRegisterInfo() const { + return static_cast<RegisterInfoPOSIX_arm64 &>(*m_register_info_interface_up); +} + +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) +bool NativeRegisterContextWindows_arm64::IsSVE(uint32_t reg_index) const { + return GetRegisterInfo().IsSVEReg(reg_index); +} + +void NativeRegisterContextWindows_arm64::ConfigureRegisterContext() { + // ConfigureRegisterContext gets called from InvalidateAllRegisters + // on every stop and configures the SVE vector length. + + Log *log = GetLog(WindowsLog::Registers); + + // If m_sve_state is found to be set to SVEState::Disabled on the first stop, + // then ConfigureRegisterContext is deemed non-operational for the lifetime of + // the current process. + if (!m_sve_header_is_valid && m_sve_state != SVEState::Disabled) { + Status error = CacheAllRegisterValues(); + if (error.Fail()) + LLDB_LOG(log, "failed to cache all register values: {0}", error); + + if (!m_sve_header_is_valid) + LLDB_LOG(log, "failed to read SVE header: {0}", error); + + if (m_sve_header_is_valid && m_sve_state == SVEState::Full) { + uint32_t vq = RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64SVE; + + if (sve::vl_valid(m_sve_header->VectorLength)) + vq = sve::vq_from_vl(m_sve_header->VectorLength); + + GetRegisterInfo().ConfigureVectorLengthSVE(vq); + } + } +} + +Status NativeRegisterContextWindows_arm64::ReadSVEHeader() { + Log *log = GetLog(WindowsLog::Registers); + Status error; + + if (m_sve_header_is_valid) + return error; + + if (m_sve_state == SVEState::Disabled) { + error = Status::FromErrorString("SVE is either unsupported or disabled"); + LLDB_LOG(log, "{0}", error); + return error; + } + + if (!m_context) { + error = Status::FromErrorString("register context is not cached"); + LLDB_LOG(log, "{0}", error); + return error; + } + + DWORD64 feature_mask = 0; + DWORD sve_feature_area_length = 0; + + if (!GetXStateFeaturesMask(m_context, &feature_mask)) { + error = Status(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "GetXStateFeaturesMask failed with error {0}", error); + return error; + } + + // If the SVE bit is unset, then SVE is in a processor-specific INITIALIZED + // state and the contents of the SVE feature area retrieved by + // LocateXStateFeature are documented as undefined. We deliberately do not + // synthesize values in this case since the values implied by the INITIALIZED + // state for SVE aren't documented. Queries pertaining to SVE registers become + // serviceable on the first stop where the SVE feature-mask bit is reported to + // be set. + if ((feature_mask & XSTATE_MASK_ARM64_SVE) == 0) { + error = Status::FromErrorString( + "SVE is in a processor-specific INITIALIZED state and the contents of " + "the SVE feature area are undefined"); + LLDB_LOG(log, "{0}", error); + return error; + } + + m_sve_header = static_cast<XSAVE_ARM64_SVE_HEADER *>(LocateXStateFeature( + m_context, XSTATE_ARM64_SVE, &sve_feature_area_length)); + + if (!m_sve_header) { + error = Status::FromErrorString("failed to locate SVE feature area"); + LLDB_LOG(log, "{0}", error); + return error; + } + + if (sve_feature_area_length < sizeof(XSAVE_ARM64_SVE_HEADER)) { + error = Status::FromErrorString("SVE feature area too small"); + LLDB_LOG(log, "expected at least {0} bytes, got {1}", + sizeof(XSAVE_ARM64_SVE_HEADER), sve_feature_area_length); + return error; + } + + m_sve_header_is_valid = true; + m_sve_state = SVEState::Full; + + return error; +} + +Status NativeRegisterContextWindows_arm64::SVERead(const uint32_t reg, + RegisterValue ®_value) { + Log *log = GetLog(WindowsLog::Registers); + Status error = CacheAllRegisterValues(); if (error.Fail()) - m_context = nullptr; + return error; + + if (!m_sve_header_is_valid) { + error = Status::FromErrorString("SVE header is unavailable"); + LLDB_LOG(log, "{0}", error); + return error; + } + + if (GetRegisterInfo().IsSVERegVG(reg)) { + reg_value.SetUInt64(GetSVERegVG()); + return error; + } + + if (GetRegisterInfo().IsSVEZReg(reg)) { + // For VL == sizeof(ARM64_NT_NEON128), Z[i] has no architectural high bits + // beyond V[i]. So, route through FPRRead to avoid touching the SVE feature + // area. + if (m_sve_header->VectorLength == sizeof(ARM64_NT_NEON128)) + return FPRRead(reg - GetRegisterInfo().GetRegNumSVEZ0() + + k_first_fpr_arm64, + reg_value); + + if (!m_sve_z_buffer_is_valid || !m_sve_z_buffer) { + error = Status::FromErrorString("SVE Z register cache is unavailable"); + LLDB_LOG(log, "{0}", error); + return error; + } + + const uint32_t vl = m_sve_header->VectorLength; + const uint32_t offset = (reg - GetRegisterInfo().GetRegNumSVEZ0()) * vl; + + reg_value.SetBytes(m_sve_z_buffer->GetBytes() + offset, vl, + endian::InlHostByteOrder()); + return error; + } + + if (GetRegisterInfo().IsSVEPReg(reg) || + reg == GetRegisterInfo().GetRegNumSVEFFR()) { + const uint32_t pl = m_sve_header->VectorLength / 8; + const uint8_t *src = reinterpret_cast<const uint8_t *>(m_sve_header) + + m_sve_header->PredicateRegisterOffset; + const uint32_t offset = (reg - GetRegisterInfo().GetRegNumSVEP0()) * pl; + reg_value.SetBytes(src + offset, pl, endian::InlHostByteOrder()); + return error; + } + + return Status::FromErrorString("unsupported SVE register"); +} + +Status NativeRegisterContextWindows_arm64::CacheSVEZRegisters() { + Log *log = GetLog(WindowsLog::Registers); + + Status error; + + if (m_sve_z_buffer_is_valid) + return error; + + if (!m_context) { + error = Status::FromErrorString("register context is not cached"); + LLDB_LOG(log, "{0}", error); + return error; + } + + if (!m_sve_header_is_valid) + error = ReadSVEHeader(); + + if (error.Fail()) + return error; + + const uint32_t vl = m_sve_header->VectorLength; + + if (vl < k_z_low_bits_size) { + error = Status::FromErrorString("invalid SVE vector length"); + LLDB_LOG(log, "{0}", error); + return error; + } + + const uint32_t num_z_regs = + GetRegisterInfo().GetRegNumSVEP0() - GetRegisterInfo().GetRegNumSVEZ0(); + + if (m_sve_z_buffer) + m_sve_z_buffer->SetByteSize(vl * num_z_regs); + else + m_sve_z_buffer = std::make_shared<DataBufferHeap>(vl * num_z_regs, 0); + + if (!m_sve_z_buffer) { + error = Status::FromErrorString("failed to allocate SVE Z buffer"); + LLDB_LOG(log, "{0}", error); + return error; + } + + // The lower 128 bits (16 bytes) are stored in the NEON V registers within the + // standard CONTEXT structure (m_context->V[n].B). The upper bits + // (VectorLength - 16 bytes) are stored contiguously in a packed array within + // the XState SVE feature area, starting at VectorRegisterOffset. Each + // register's high bits are stored back-to-back with no padding, so register + // n's high bits begin at VectorRegisterOffset + (n * z_high_bits_size). To + // reconstruct each full Z register, we interleave these two sources. For each + // register, we copy the 16-byte V register value into the output buffer first + // and then append the corresponding high bits from the XState area, yielding + // a contiguous VectorLength-byte representation. + const uint32_t z_high_bits_size = vl - k_z_low_bits_size; + uint8_t *dst = m_sve_z_buffer->GetBytes(); + const uint8_t *src = reinterpret_cast<const uint8_t *>(m_sve_header) + + m_sve_header->VectorRegisterOffset; + + for (uint32_t reg = 0; reg < num_z_regs; ++reg) { + // Copy lower 128 bits from V register. + memcpy(dst, m_context->V[reg].B, k_z_low_bits_size); + dst += k_z_low_bits_size; + // Copy high bits from packed SVE extended state. + memcpy(dst, src, z_high_bits_size); + dst += z_high_bits_size; + src += z_high_bits_size; + } + + m_sve_z_buffer_is_valid = true; return error; } +Status +NativeRegisterContextWindows_arm64::SVEWrite(const uint32_t reg, + const RegisterValue ®_value) { + Log *log = GetLog(WindowsLog::Registers); + Status error; + + if (GetRegisterInfo().IsSVERegVG(reg)) { + // The vector length is constant and is determined at the time the process + // is created. + return error; + } + + auto cleanup = llvm::make_scope_exit([&]() { + m_context = nullptr; + m_sve_header = nullptr; + m_sve_header_is_valid = false; + m_sve_z_buffer_is_valid = false; + }); + + error = CacheAllRegisterValues(); + if (error.Fail()) + return error; + + if (!m_context) { + error = Status::FromErrorString("register context is not cached"); + LLDB_LOG(log, "{0}", error); + return error; + } + + if (!m_sve_header_is_valid) { + error = Status::FromErrorString("failed to read SVE header"); + LLDB_LOG(log, "{0}", error); + return error; + } + + if (GetRegisterInfo().IsSVEZReg(reg)) { + // For VL == sizeof(ARM64_NT_NEON128), Z[i] has no architectural high bits + // beyond V[i]. So, route through FPRWrite to avoid touching the SVE feature + // area. + if (m_sve_header->VectorLength == sizeof(ARM64_NT_NEON128)) + return FPRWrite(reg - GetRegisterInfo().GetRegNumSVEZ0() + + k_first_fpr_arm64, + reg_value); + + const uint32_t vl = m_sve_header->VectorLength; + + if (vl < k_z_low_bits_size) { + error = Status::FromErrorString("invalid SVE vector length"); + LLDB_LOG(log, "{0}", error); + return error; + } + + const uint32_t z_index = reg - GetRegisterInfo().GetRegNumSVEZ0(); + const uint32_t z_high_bits_size = vl - k_z_low_bits_size; + + const uint8_t *src = + reinterpret_cast<const uint8_t *>(reg_value.GetBytes()); + + if (!src) { + error = Status::FromErrorString("invalid SVE Z register value"); + LLDB_LOG(log, "{0}", error); + return error; + } + + uint8_t *dst = reinterpret_cast<uint8_t *>(m_sve_header) + + m_sve_header->VectorRegisterOffset + + (z_index * z_high_bits_size); + + // Copy lower 128 bits to V register. + memcpy(m_context->V[z_index].B, src, k_z_low_bits_size); + + // Copy high bits to packed SVE extended state. + memcpy(dst, src + k_z_low_bits_size, z_high_bits_size); + + return SetThreadContextHelper(GetThreadHandle(), m_context); + } + + if (GetRegisterInfo().IsSVEPReg(reg) || + reg == GetRegisterInfo().GetRegNumSVEFFR()) { + const uint32_t pl = m_sve_header->VectorLength / 8; + const uint32_t offset = (reg - GetRegisterInfo().GetRegNumSVEP0()) * pl; + + const uint8_t *src = + reinterpret_cast<const uint8_t *>(reg_value.GetBytes()); + + if (!src) { + error = Status::FromErrorString("invalid SVE predicate register value"); + LLDB_LOG(log, "{0}", error); + return error; + } + + uint8_t *dst = reinterpret_cast<uint8_t *>(m_sve_header) + + m_sve_header->PredicateRegisterOffset + offset; + memcpy(dst, src, pl); + + return SetThreadContextHelper(GetThreadHandle(), m_context); + } + + return Status::FromErrorString("unsupported SVE register"); +} +#endif + #endif // defined(__aarch64__) || defined(_M_ARM64) diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h index 4cfb3bf13c91b..f7c47303ddad2 100644 --- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h +++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h @@ -18,6 +18,10 @@ #include "lldb/Host/windows/windows.h" +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) +#include "Plugins/Process/Utility/LinuxPTraceDefines_arm64sve.h" +#endif + namespace lldb_private { class NativeThreadWindows; @@ -26,8 +30,9 @@ class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows, public NativeRegisterContextDBReg_arm64 { public: - NativeRegisterContextWindows_arm64(const ArchSpec &target_arch, - NativeThreadProtocol &native_thread); + NativeRegisterContextWindows_arm64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread, + std::unique_ptr<RegisterInfoPOSIX_arm64> register_info_up); uint32_t GetRegisterSetCount() const override; @@ -54,10 +59,26 @@ class NativeRegisterContextWindows_arm64 Status FPRWrite(const uint32_t reg, const RegisterValue ®_value); +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + Status SVERead(const uint32_t reg, RegisterValue ®_value); + + Status SVEWrite(const uint32_t reg, const RegisterValue ®_value); +#endif + private: PCONTEXT m_context; std::shared_ptr<DataBufferHeap> m_context_buffer; +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + XSAVE_ARM64_SVE_HEADER *m_sve_header; + bool m_sve_header_is_valid; + SVEState m_sve_state; + std::shared_ptr<DataBufferHeap> m_sve_z_buffer; + bool m_sve_z_buffer_is_valid; + + static constexpr uint32_t k_z_low_bits_size = sizeof(ARM64_NT_NEON128); +#endif + bool IsGPR(uint32_t reg_index) const; bool IsFPR(uint32_t reg_index) const; @@ -67,6 +88,20 @@ class NativeRegisterContextWindows_arm64 llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; Status CacheAllRegisterValues(); + + RegisterInfoPOSIX_arm64 &GetRegisterInfo() const; + +#if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + bool IsSVE(uint32_t reg_index) const; + + uint32_t GetSVERegVG() const { return m_sve_header->VectorLength / 8; } + + void ConfigureRegisterContext(); + + Status ReadSVEHeader(); + + Status CacheSVEZRegisters(); +#endif }; } // namespace lldb_private diff --git a/lldb/test/API/commands/register/aarch64_sve_registers_windows/Makefile b/lldb/test/API/commands/register/aarch64_sve_registers_windows/Makefile new file mode 100644 index 0000000000000..0e40ec73d0ed0 --- /dev/null +++ b/lldb/test/API/commands/register/aarch64_sve_registers_windows/Makefile @@ -0,0 +1,5 @@ +C_SOURCES := main.c + +CFLAGS_EXTRAS ?= -march=armv8-a+sve + +include Makefile.rules diff --git a/lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegisters.py b/lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegisters.py new file mode 100644 index 0000000000000..8bfdbafb43969 --- /dev/null +++ b/lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegisters.py @@ -0,0 +1,170 @@ +""" +Test AArch64 SVE registers on Windows. + +NOTE: The default non-streaming SVE vector length is 128 bits, so Z register +reads and writes currently go through the V register read and write paths. As a +result, the interleaving of the low and high bits of Z registers is NOT +exercised. P/FFR read and P write paths ARE exercised. + +TODO: Add coverage for a wider vector length. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +import lldbsuite.test.cpu_feature as cpu_feature + + +class SVETestCase(TestBase): + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # main() + self.line1 = line_number("main.c", "// breakpoint 1") + + def run_sve_test(self): + # Set breakpoints + self.runCmd("breakpoint set -f main.c -l " + str(self.line1)) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped at 'breakpoint 1', with all SVE registers set. + + # The default non-streaming mode vector length is 128 bits (16 bytes). + byte_length = 16 + + # Load 'zregs' with a byte pattern consisting of register number + # followed by 0-7: + # 00 00 00 01 00 02 00 03 ... 00 07 + # 01 00 01 01 01 02 01 03 ... 01 07 + # ... + # 31 00 31 01 31 02 31 03 ... 31 07 + zregs = ["" for i in range(32)] + + for i in range(32): + bytes_list = [] + for j in range(byte_length // 2): + bytes_list.append("0x%02x" % i) + bytes_list.append("0x%02x" % j) + zregs[i] = "{" + " ".join(bytes_list) + "}" + + # Load 'pregs' with a byte pattern consisting of register number + # followed by 0: + # 00 00 + # 01 00 + # ... + # 31 00 + pregs = ["" for i in range(16)] + + for i in range(16): + bytes_list = [] + for j in range(byte_length // 16): + bytes_list.append("0x%02x" % i) + bytes_list.append("0x%02x" % j) + pregs[i] = "{" + " ".join(bytes_list) + "}" + + # load 'ffr' bytes with a byte pattern consisting of all 1s: + # ff ff + bytes_list = [] + for i in range(byte_length // 8): + bytes_list.append("0xff") + ffr = "{" + " ".join(bytes_list) + "}" + + # Test that 'vg' has the correct value. + self.expect( + "register read vg", + "vg is correct", + substrs=["vg = 0x00000000000000%02x" % (byte_length // 8)], + ) + + # Test that Z registers have the correct values. + for i in range(32): + self.expect( + "register read z" + str(i), + "sve register z" + str(i) + " is valid", + substrs=["z" + str(i) + " = " + zregs[i]], + ) + + # Test that P registers have the correct values. + for i in range(16): + self.expect( + "register read p" + str(i), + "sve register p" + str(i) + " is valid", + substrs=["p" + str(i) + " = " + pregs[i]], + ) + + # Test that FFR has the correct value. + self.expect( + "register read ffr", + "sve register ffr is valid", + substrs=["ffr" + " = " + ffr], + ) + + # Write 'z7', then read it back. + # 'z7' has value '0x07000701...'. Write it with something different, and + # check it. The saved value should be the value that we wrote. + bytes_pattern = [ + "00", + "11", + "22", + "33", + "44", + "55", + "66", + "77", + "88", + "99", + "aa", + "bb", + "cc", + "dd", + "ee", + "ff", + ] + + bytes_list = [] + bytes_list.extend("0x" + b for b in bytes_pattern) + my_z7 = "{" + " ".join(bytes_list) + "}" + + self.runCmd("register write z7 '" + my_z7 + "'") + self.expect( + "register read z7", + "z7 has correct value after write", + substrs=["z7 = " + my_z7], + ) + + # Write 'p7', then read it back. + # 'p7' has value '0x0700'. Write it with something different, and check + # it. The saved value should be the value that we wrote. + bytes_pattern = [ + "00", + "11", + ] + + bytes_list = [] + bytes_list.extend("0x" + b for b in bytes_pattern) + my_p7 = "{" + " ".join(bytes_list) + "}" + + self.runCmd("register write p7 '" + my_p7 + "'") + self.expect( + "register read p7", + "p7 has correct value after write", + substrs=["p7 = " + my_p7], + ) + + # Currently, the test is supported only on WoA devices with SVE support when + # run through lldb-server and when the debugger is built with Windows SDK + # 10.0.26100 (Windows 11 24H2) or later. One caveat, however, is that if the + # debugger was built with an older Windows SDK, the test will still run on a + # WoA device with SVE support through lldb-server but it will fail. + @skipUnlessWindows + @skipUnlessFeature(cpu_feature.AArch64.SVE) + @skipIf(remote=False) + def test_sve(self): + """Test SVE register access, non-streaming""" + self.build() + exe = self.getBuildArtifact("a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + self.run_sve_test() diff --git a/lldb/test/API/commands/register/aarch64_sve_registers_windows/main.c b/lldb/test/API/commands/register/aarch64_sve_registers_windows/main.c new file mode 100644 index 0000000000000..6f89ac663b0f6 --- /dev/null +++ b/lldb/test/API/commands/register/aarch64_sve_registers_windows/main.c @@ -0,0 +1,152 @@ +#include <arm_sve.h> +#include <stdlib.h> + +#define NUM_Z_REGS 32 +#define NUM_P_REGS 16 + +void setup_z_and_p_regs() { + uint8_t *zregs; // memory for z registers + uint8_t *pregs; // memory for p registers + uint8_t *reg_pointer; // pointer to current register memory location + int i, j; + int z_size = svcntb(); + int p_size = z_size / 8; + + zregs = malloc(NUM_Z_REGS * z_size); + pregs = malloc(NUM_P_REGS * p_size); + + // Load 'zregs' with a byte pattern consisting of register number followed by + // 0-(up to)7f depending on vector size: + // 00 00 00 01 00 02 00 03 ... 00 7f + // 01 00 01 01 01 02 01 03 ... 01 7f + // ... + // 31 00 31 01 31 02 31 03 ... 31 7f + for (i = 0; i < NUM_Z_REGS; ++i) { + for (j = 0; j < z_size; j += 2) { + zregs[i * z_size + j] = i; + zregs[i * z_size + j + 1] = j / 2; + } + } + + // Load 'pregs' with a byte pattern consisting of register number followed by + // 0-(up to)7f depending on vector size: + // 00 00 00 01 00 02 00 03 ... 00 7f + // 01 00 01 01 01 02 01 03 ... 01 7f + // ... + // 31 00 31 01 31 02 31 03 ... 31 7f + for (i = 0; i < NUM_P_REGS; ++i) { + for (j = 0; j < p_size; j += 2) { + pregs[i * p_size + j] = i; + pregs[i * p_size + j + 1] = j / 2; + } + } + + // Copy values from memory to Z registers, using 'ldr'. + reg_pointer = zregs; + asm("ldr z0, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z1, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z2, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z3, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z4, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z5, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z6, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z7, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z8, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z9, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z10, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z11, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z12, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z13, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z14, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z15, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z16, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z17, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z18, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z19, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z20, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z21, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z22, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z23, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z24, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z25, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z26, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z27, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z28, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z29, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z30, [%0]" ::"r"(reg_pointer) :); + reg_pointer += z_size; + asm("ldr z31, [%0]" ::"r"(reg_pointer) :); + + // Copy values from memory to P registers, using 'ldr'. + reg_pointer = pregs; + asm("ldr p0, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p1, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p2, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p3, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p4, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p5, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p6, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p7, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p8, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p9, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p10, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p11, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p12, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p13, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p14, [%0]" ::"r"(reg_pointer) :); + reg_pointer += p_size; + asm("ldr p15, [%0]" ::"r"(reg_pointer) :); + + asm("setffr"); +} + +int main() { + // Set up Z and P registers. + setup_z_and_p_regs(); + + return 0; // breakpoint 1 +} >From 63abc262e41e42fd819fe5b55b51ab531efce8e8 Mon Sep 17 00:00:00 2001 From: Ayush Sahay <[email protected]> Date: Fri, 26 Jun 2026 02:55:32 +0530 Subject: [PATCH 2/4] Fix formatting and rename TestSVERegisters.py --- .../Python/lldbsuite/test/cpu_feature.py | 17 ++++++++++++----- ...stSVERegisters.py => TestSVERegistersWoA.py} | 1 - 2 files changed, 12 insertions(+), 6 deletions(-) rename lldb/test/API/commands/register/aarch64_sve_registers_windows/{TestSVERegisters.py => TestSVERegistersWoA.py} (99%) diff --git a/lldb/packages/Python/lldbsuite/test/cpu_feature.py b/lldb/packages/Python/lldbsuite/test/cpu_feature.py index 6baa7d9615e02..3dbaea119f32e 100644 --- a/lldb/packages/Python/lldbsuite/test/cpu_feature.py +++ b/lldb/packages/Python/lldbsuite/test/cpu_feature.py @@ -8,7 +8,12 @@ PF_ARM_SVE_INSTRUCTIONS_AVAILABLE = 46 class CPUFeature: - def __init__(self, linux_cpu_info_flag: str = None, darwin_sysctl_key: str = None, windows_processor_feature: int = None): + def __init__( + self, + linux_cpu_info_flag: str = None, + darwin_sysctl_key: str = None, + windows_processor_feature: int = None, + ): self.cpu_info_flag = linux_cpu_info_flag self.sysctl_key = darwin_sysctl_key self.windows_processor_feature = windows_processor_feature @@ -67,7 +72,7 @@ def _is_supported_darwin(self, cmd_runner): # requires the .NET CLR and the CSC compiler to be available. Neither is # guaranteed. # TODO: Replace the PowerShell chain with a probe that calls - # 'IsProcessorFeaturePresent' directly. + # 'IsProcessorFeaturePresent' directly. def _is_supported_windows(self, cmd_runner): import base64 @@ -79,7 +84,7 @@ def _is_supported_windows(self, cmd_runner): "Add-Type -TypeDefinition '" "using System; using System.Runtime.InteropServices; " "public class WinAPI { " - "[DllImport(\"kernel32.dll\")] " + '[DllImport("kernel32.dll")] ' "public static extern bool IsProcessorFeaturePresent(uint f); }'; " f"[WinAPI]::IsProcessorFeaturePresent({self.windows_processor_feature})" ) @@ -89,8 +94,10 @@ def _is_supported_windows(self, cmd_runner): cmd = f"powershell -EncodedCommand {encoded}" err, retcode, output = cmd_runner(cmd) if err.Fail() or retcode != 0: - return ("Windows SVE detection via PowerShell failed " - "(retcode={0}, output={1!r})".format(retcode, output)), False + return ( + "Windows SVE detection via PowerShell failed " + "(retcode={0}, output={1!r})".format(retcode, output) + ), False return None, (output.strip().lower() == "true") diff --git a/lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegisters.py b/lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegistersWoA.py similarity index 99% rename from lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegisters.py rename to lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegistersWoA.py index 8bfdbafb43969..1a930b423b81a 100644 --- a/lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegisters.py +++ b/lldb/test/API/commands/register/aarch64_sve_registers_windows/TestSVERegistersWoA.py @@ -16,7 +16,6 @@ class SVETestCase(TestBase): - def setUp(self): # Call super's setUp(). TestBase.setUp(self) >From 00622b580cd429d6aec71eaedcad28a6023a2291 Mon Sep 17 00:00:00 2001 From: Ayush Sahay <[email protected]> Date: Fri, 26 Jun 2026 03:15:07 +0530 Subject: [PATCH 3/4] Fix formatting --- lldb/packages/Python/lldbsuite/test/cpu_feature.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/packages/Python/lldbsuite/test/cpu_feature.py b/lldb/packages/Python/lldbsuite/test/cpu_feature.py index 3dbaea119f32e..3a546706dac7d 100644 --- a/lldb/packages/Python/lldbsuite/test/cpu_feature.py +++ b/lldb/packages/Python/lldbsuite/test/cpu_feature.py @@ -7,6 +7,7 @@ PF_ARM_SVE_INSTRUCTIONS_AVAILABLE = 46 + class CPUFeature: def __init__( self, >From ebab7d6ef85afa9460d269410042d34c59c7dbe8 Mon Sep 17 00:00:00 2001 From: Ayush Sahay <[email protected]> Date: Sat, 27 Jun 2026 01:20:02 +0530 Subject: [PATCH 4/4] Remove dead null check --- .../Windows/Common/NativeRegisterContextWindows_arm64.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp index 0e2ee4d2f93cd..95ba17049147c 100644 --- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp @@ -1124,12 +1124,6 @@ Status NativeRegisterContextWindows_arm64::CacheSVEZRegisters() { else m_sve_z_buffer = std::make_shared<DataBufferHeap>(vl * num_z_regs, 0); - if (!m_sve_z_buffer) { - error = Status::FromErrorString("failed to allocate SVE Z buffer"); - LLDB_LOG(log, "{0}", error); - return error; - } - // The lower 128 bits (16 bytes) are stored in the NEON V registers within the // standard CONTEXT structure (m_context->V[n].B). The upper bits // (VectorLength - 16 bytes) are stored contiguously in a packed array within _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
