[Lldb-commits] [lldb] a59444a - [LLDB] [Windows] Initial support for ARM register contexts

Martin Storsjo via lldb-commits lldb-commits at lists.llvm.org
Mon Oct 21 01:02:17 PDT 2019


Author: Martin Storsjo
Date: 2019-10-21T08:02:34Z
New Revision: a59444a35608988e727fe3761e34f1fad6097617

URL: https://github.com/llvm/llvm-project/commit/a59444a35608988e727fe3761e34f1fad6097617
DIFF: https://github.com/llvm/llvm-project/commit/a59444a35608988e727fe3761e34f1fad6097617.diff

LOG: [LLDB] [Windows] Initial support for ARM register contexts

Differential Revision: https://reviews.llvm.org/D69226

llvm-svn: 375392

Added: 
    lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp
    lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h
    lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp
    lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h
    lldb/test/Shell/Register/Inputs/arm-fp-read.cpp
    lldb/test/Shell/Register/Inputs/arm-gp-read.cpp
    lldb/test/Shell/Register/arm-fp-read.test
    lldb/test/Shell/Register/arm-gp-read.test

Modified: 
    lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt
    lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
    llvm/utils/lit/lit/llvm/config.py

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt b/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt
index d0d3fcbee6f4..876bc8cab966 100644
--- a/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt
@@ -4,6 +4,7 @@ add_lldb_library(lldbPluginProcessWindowsCommon PLUGIN
   LocalDebugDelegate.cpp
   NativeProcessWindows.cpp
   NativeRegisterContextWindows.cpp
+  NativeRegisterContextWindows_arm.cpp
   NativeRegisterContextWindows_arm64.cpp
   NativeRegisterContextWindows_i386.cpp
   NativeRegisterContextWindows_WoW64.cpp
@@ -14,10 +15,10 @@ add_lldb_library(lldbPluginProcessWindowsCommon PLUGIN
   ProcessWindowsLog.cpp
   RegisterContextWindows.cpp
   TargetThreadWindows.cpp
+  arm/RegisterContextWindows_arm.cpp
   arm64/RegisterContextWindows_arm64.cpp
   x64/RegisterContextWindows_x64.cpp
   x86/RegisterContextWindows_x86.cpp
-  # TODO add support for ARM (NT)
 
   LINK_LIBS
     lldbCore

diff  --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp
new file mode 100644
index 000000000000..d25b08f7ecba
--- /dev/null
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp
@@ -0,0 +1,644 @@
+//===-- NativeRegisterContextWindows_arm.cpp --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__arm__) || defined(_M_ARM)
+
+#include "NativeRegisterContextWindows_arm.h"
+#include "NativeThreadWindows.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
+#include "ProcessWindowsLog.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/windows/HostThreadWindows.h"
+#include "lldb/Host/windows/windows.h"
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define REG_CONTEXT_SIZE sizeof(::CONTEXT)
+
+namespace {
+static const uint32_t g_gpr_regnums_arm[] = {
+    gpr_r0_arm,         gpr_r1_arm,   gpr_r2_arm,  gpr_r3_arm, gpr_r4_arm,
+    gpr_r5_arm,         gpr_r6_arm,   gpr_r7_arm,  gpr_r8_arm, gpr_r9_arm,
+    gpr_r10_arm,        gpr_r11_arm,  gpr_r12_arm, gpr_sp_arm, gpr_lr_arm,
+    gpr_pc_arm,         gpr_cpsr_arm,
+    LLDB_INVALID_REGNUM // Register set must be terminated with this flag
+};
+static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) ==
+                  k_num_gpr_registers_arm,
+              "g_gpr_regnums_arm has wrong number of register infos");
+
+static const uint32_t g_fpr_regnums_arm[] = {
+    fpu_s0_arm,         fpu_s1_arm,  fpu_s2_arm,  fpu_s3_arm,  fpu_s4_arm,
+    fpu_s5_arm,         fpu_s6_arm,  fpu_s7_arm,  fpu_s8_arm,  fpu_s9_arm,
+    fpu_s10_arm,        fpu_s11_arm, fpu_s12_arm, fpu_s13_arm, fpu_s14_arm,
+    fpu_s15_arm,        fpu_s16_arm, fpu_s17_arm, fpu_s18_arm, fpu_s19_arm,
+    fpu_s20_arm,        fpu_s21_arm, fpu_s22_arm, fpu_s23_arm, fpu_s24_arm,
+    fpu_s25_arm,        fpu_s26_arm, fpu_s27_arm, fpu_s28_arm, fpu_s29_arm,
+    fpu_s30_arm,        fpu_s31_arm,
+
+    fpu_d0_arm,         fpu_d1_arm,  fpu_d2_arm,  fpu_d3_arm,  fpu_d4_arm,
+    fpu_d5_arm,         fpu_d6_arm,  fpu_d7_arm,  fpu_d8_arm,  fpu_d9_arm,
+    fpu_d10_arm,        fpu_d11_arm, fpu_d12_arm, fpu_d13_arm, fpu_d14_arm,
+    fpu_d15_arm,        fpu_d16_arm, fpu_d17_arm, fpu_d18_arm, fpu_d19_arm,
+    fpu_d20_arm,        fpu_d21_arm, fpu_d22_arm, fpu_d23_arm, fpu_d24_arm,
+    fpu_d25_arm,        fpu_d26_arm, fpu_d27_arm, fpu_d28_arm, fpu_d29_arm,
+    fpu_d30_arm,        fpu_d31_arm,
+
+    fpu_q0_arm,         fpu_q1_arm,  fpu_q2_arm,  fpu_q3_arm,  fpu_q4_arm,
+    fpu_q5_arm,         fpu_q6_arm,  fpu_q7_arm,  fpu_q8_arm,  fpu_q9_arm,
+    fpu_q10_arm,        fpu_q11_arm, fpu_q12_arm, fpu_q13_arm, fpu_q14_arm,
+    fpu_q15_arm,
+
+    fpu_fpscr_arm,
+    LLDB_INVALID_REGNUM // Register set must be terminated with this flag
+};
+static_assert(((sizeof g_fpr_regnums_arm / sizeof g_fpr_regnums_arm[0]) - 1) ==
+                  k_num_fpr_registers_arm,
+              "g_fpu_regnums_arm has wrong number of register infos");
+
+static const RegisterSet g_reg_sets_arm[] = {
+    {"General Purpose Registers", "gpr",
+     llvm::array_lengthof(g_gpr_regnums_arm) - 1, g_gpr_regnums_arm},
+    {"Floating Point Registers", "fpr",
+     llvm::array_lengthof(g_fpr_regnums_arm) - 1, g_fpr_regnums_arm},
+};
+
+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_arm(target_arch);
+}
+
+static Status GetThreadContextHelper(lldb::thread_t thread_handle,
+                                     PCONTEXT context_ptr,
+                                     const DWORD control_flag) {
+  Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
+  Status error;
+
+  memset(context_ptr, 0, sizeof(::CONTEXT));
+  context_ptr->ContextFlags = control_flag;
+  if (!::GetThreadContext(thread_handle, context_ptr)) {
+    error.SetError(GetLastError(), eErrorTypeWin32);
+    LLDB_LOG(log, "{0} GetThreadContext failed with error {1}", __FUNCTION__,
+             error);
+    return error;
+  }
+  return Status();
+}
+
+static Status SetThreadContextHelper(lldb::thread_t thread_handle,
+                                     PCONTEXT context_ptr) {
+  Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
+  Status error;
+  // It's assumed that the thread has stopped.
+  if (!::SetThreadContext(thread_handle, context_ptr)) {
+    error.SetError(GetLastError(), eErrorTypeWin32);
+    LLDB_LOG(log, "{0} SetThreadContext failed with error {1}", __FUNCTION__,
+             error);
+    return error;
+  }
+  return Status();
+}
+
+std::unique_ptr<NativeRegisterContextWindows>
+NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
+    const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+  // TODO: Register context for a WoW64 application?
+
+  // Register context for a native 64-bit application.
+  return std::make_unique<NativeRegisterContextWindows_arm>(target_arch,
+                                                            native_thread);
+}
+
+NativeRegisterContextWindows_arm::NativeRegisterContextWindows_arm(
+    const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+    : NativeRegisterContextWindows(native_thread,
+                                   CreateRegisterInfoInterface(target_arch)) {}
+
+bool NativeRegisterContextWindows_arm::IsGPR(uint32_t reg_index) const {
+  return (reg_index >= k_first_gpr_arm && reg_index <= k_last_gpr_arm);
+}
+
+bool NativeRegisterContextWindows_arm::IsFPR(uint32_t reg_index) const {
+  return (reg_index >= k_first_fpr_arm && reg_index <= k_last_fpr_arm);
+}
+
+uint32_t NativeRegisterContextWindows_arm::GetRegisterSetCount() const {
+  return k_num_register_sets;
+}
+
+const RegisterSet *
+NativeRegisterContextWindows_arm::GetRegisterSet(uint32_t set_index) const {
+  if (set_index >= k_num_register_sets)
+    return nullptr;
+  return &g_reg_sets_arm[set_index];
+}
+
+Status NativeRegisterContextWindows_arm::GPRRead(const uint32_t reg,
+                                                 RegisterValue &reg_value) {
+  ::CONTEXT tls_context;
+  DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER;
+  Status error =
+      GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag);
+  if (error.Fail())
+    return error;
+
+  switch (reg) {
+  case gpr_r0_arm:
+    reg_value.SetUInt32(tls_context.R0);
+    break;
+  case gpr_r1_arm:
+    reg_value.SetUInt32(tls_context.R1);
+    break;
+  case gpr_r2_arm:
+    reg_value.SetUInt32(tls_context.R2);
+    break;
+  case gpr_r3_arm:
+    reg_value.SetUInt32(tls_context.R3);
+    break;
+  case gpr_r4_arm:
+    reg_value.SetUInt32(tls_context.R4);
+    break;
+  case gpr_r5_arm:
+    reg_value.SetUInt32(tls_context.R5);
+    break;
+  case gpr_r6_arm:
+    reg_value.SetUInt32(tls_context.R6);
+    break;
+  case gpr_r7_arm:
+    reg_value.SetUInt32(tls_context.R7);
+    break;
+  case gpr_r8_arm:
+    reg_value.SetUInt32(tls_context.R8);
+    break;
+  case gpr_r9_arm:
+    reg_value.SetUInt32(tls_context.R9);
+    break;
+  case gpr_r10_arm:
+    reg_value.SetUInt32(tls_context.R10);
+    break;
+  case gpr_r11_arm:
+    reg_value.SetUInt32(tls_context.R11);
+    break;
+  case gpr_r12_arm:
+    reg_value.SetUInt32(tls_context.R12);
+    break;
+  case gpr_sp_arm:
+    reg_value.SetUInt32(tls_context.Sp);
+    break;
+  case gpr_lr_arm:
+    reg_value.SetUInt32(tls_context.Lr);
+    break;
+  case gpr_pc_arm:
+    reg_value.SetUInt32(tls_context.Pc);
+    break;
+  case gpr_cpsr_arm:
+    reg_value.SetUInt32(tls_context.Cpsr);
+    break;
+  }
+
+  return error;
+}
+
+Status
+NativeRegisterContextWindows_arm::GPRWrite(const uint32_t reg,
+                                           const RegisterValue &reg_value) {
+  ::CONTEXT tls_context;
+  DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER;
+  auto thread_handle = GetThreadHandle();
+  Status error =
+      GetThreadContextHelper(thread_handle, &tls_context, context_flag);
+  if (error.Fail())
+    return error;
+
+  switch (reg) {
+  case gpr_r0_arm:
+    tls_context.R0 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r1_arm:
+    tls_context.R1 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r2_arm:
+    tls_context.R2 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r3_arm:
+    tls_context.R3 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r4_arm:
+    tls_context.R4 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r5_arm:
+    tls_context.R5 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r6_arm:
+    tls_context.R6 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r7_arm:
+    tls_context.R7 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r8_arm:
+    tls_context.R8 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r9_arm:
+    tls_context.R9 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r10_arm:
+    tls_context.R10 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r11_arm:
+    tls_context.R11 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r12_arm:
+    tls_context.R12 = reg_value.GetAsUInt32();
+    break;
+  case gpr_sp_arm:
+    tls_context.Sp = reg_value.GetAsUInt32();
+    break;
+  case gpr_lr_arm:
+    tls_context.Lr = reg_value.GetAsUInt32();
+    break;
+  case gpr_pc_arm:
+    tls_context.Pc = reg_value.GetAsUInt32();
+    break;
+  case gpr_cpsr_arm:
+    tls_context.Cpsr = reg_value.GetAsUInt32();
+    break;
+  }
+
+  return SetThreadContextHelper(thread_handle, &tls_context);
+}
+
+Status NativeRegisterContextWindows_arm::FPRRead(const uint32_t reg,
+                                                 RegisterValue &reg_value) {
+  ::CONTEXT tls_context;
+  DWORD context_flag = CONTEXT_CONTROL | CONTEXT_FLOATING_POINT;
+  Status error =
+      GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag);
+  if (error.Fail())
+    return error;
+
+  switch (reg) {
+  case fpu_s0_arm:
+  case fpu_s1_arm:
+  case fpu_s2_arm:
+  case fpu_s3_arm:
+  case fpu_s4_arm:
+  case fpu_s5_arm:
+  case fpu_s6_arm:
+  case fpu_s7_arm:
+  case fpu_s8_arm:
+  case fpu_s9_arm:
+  case fpu_s10_arm:
+  case fpu_s11_arm:
+  case fpu_s12_arm:
+  case fpu_s13_arm:
+  case fpu_s14_arm:
+  case fpu_s15_arm:
+  case fpu_s16_arm:
+  case fpu_s17_arm:
+  case fpu_s18_arm:
+  case fpu_s19_arm:
+  case fpu_s20_arm:
+  case fpu_s21_arm:
+  case fpu_s22_arm:
+  case fpu_s23_arm:
+  case fpu_s24_arm:
+  case fpu_s25_arm:
+  case fpu_s26_arm:
+  case fpu_s27_arm:
+  case fpu_s28_arm:
+  case fpu_s29_arm:
+  case fpu_s30_arm:
+  case fpu_s31_arm:
+    reg_value.SetUInt32(tls_context.S[reg - fpu_s0_arm],
+                        RegisterValue::eTypeFloat);
+    break;
+
+  case fpu_d0_arm:
+  case fpu_d1_arm:
+  case fpu_d2_arm:
+  case fpu_d3_arm:
+  case fpu_d4_arm:
+  case fpu_d5_arm:
+  case fpu_d6_arm:
+  case fpu_d7_arm:
+  case fpu_d8_arm:
+  case fpu_d9_arm:
+  case fpu_d10_arm:
+  case fpu_d11_arm:
+  case fpu_d12_arm:
+  case fpu_d13_arm:
+  case fpu_d14_arm:
+  case fpu_d15_arm:
+  case fpu_d16_arm:
+  case fpu_d17_arm:
+  case fpu_d18_arm:
+  case fpu_d19_arm:
+  case fpu_d20_arm:
+  case fpu_d21_arm:
+  case fpu_d22_arm:
+  case fpu_d23_arm:
+  case fpu_d24_arm:
+  case fpu_d25_arm:
+  case fpu_d26_arm:
+  case fpu_d27_arm:
+  case fpu_d28_arm:
+  case fpu_d29_arm:
+  case fpu_d30_arm:
+  case fpu_d31_arm:
+    reg_value.SetUInt64(tls_context.D[reg - fpu_d0_arm],
+                        RegisterValue::eTypeDouble);
+    break;
+
+  case fpu_q0_arm:
+  case fpu_q1_arm:
+  case fpu_q2_arm:
+  case fpu_q3_arm:
+  case fpu_q4_arm:
+  case fpu_q5_arm:
+  case fpu_q6_arm:
+  case fpu_q7_arm:
+  case fpu_q8_arm:
+  case fpu_q9_arm:
+  case fpu_q10_arm:
+  case fpu_q11_arm:
+  case fpu_q12_arm:
+  case fpu_q13_arm:
+  case fpu_q14_arm:
+  case fpu_q15_arm:
+    reg_value.SetBytes(&tls_context.Q[reg - fpu_q0_arm], 16,
+                       endian::InlHostByteOrder());
+    break;
+
+  case fpu_fpscr_arm:
+    reg_value.SetUInt32(tls_context.Fpscr);
+    break;
+  }
+
+  return error;
+}
+
+Status
+NativeRegisterContextWindows_arm::FPRWrite(const uint32_t reg,
+                                           const RegisterValue &reg_value) {
+  ::CONTEXT tls_context;
+  DWORD context_flag = CONTEXT_CONTROL | CONTEXT_FLOATING_POINT;
+  auto thread_handle = GetThreadHandle();
+  Status error =
+      GetThreadContextHelper(thread_handle, &tls_context, context_flag);
+  if (error.Fail())
+    return error;
+
+  switch (reg) {
+  case fpu_s0_arm:
+  case fpu_s1_arm:
+  case fpu_s2_arm:
+  case fpu_s3_arm:
+  case fpu_s4_arm:
+  case fpu_s5_arm:
+  case fpu_s6_arm:
+  case fpu_s7_arm:
+  case fpu_s8_arm:
+  case fpu_s9_arm:
+  case fpu_s10_arm:
+  case fpu_s11_arm:
+  case fpu_s12_arm:
+  case fpu_s13_arm:
+  case fpu_s14_arm:
+  case fpu_s15_arm:
+  case fpu_s16_arm:
+  case fpu_s17_arm:
+  case fpu_s18_arm:
+  case fpu_s19_arm:
+  case fpu_s20_arm:
+  case fpu_s21_arm:
+  case fpu_s22_arm:
+  case fpu_s23_arm:
+  case fpu_s24_arm:
+  case fpu_s25_arm:
+  case fpu_s26_arm:
+  case fpu_s27_arm:
+  case fpu_s28_arm:
+  case fpu_s29_arm:
+  case fpu_s30_arm:
+  case fpu_s31_arm:
+    tls_context.S[reg - fpu_s0_arm] = reg_value.GetAsUInt32();
+    break;
+
+  case fpu_d0_arm:
+  case fpu_d1_arm:
+  case fpu_d2_arm:
+  case fpu_d3_arm:
+  case fpu_d4_arm:
+  case fpu_d5_arm:
+  case fpu_d6_arm:
+  case fpu_d7_arm:
+  case fpu_d8_arm:
+  case fpu_d9_arm:
+  case fpu_d10_arm:
+  case fpu_d11_arm:
+  case fpu_d12_arm:
+  case fpu_d13_arm:
+  case fpu_d14_arm:
+  case fpu_d15_arm:
+  case fpu_d16_arm:
+  case fpu_d17_arm:
+  case fpu_d18_arm:
+  case fpu_d19_arm:
+  case fpu_d20_arm:
+  case fpu_d21_arm:
+  case fpu_d22_arm:
+  case fpu_d23_arm:
+  case fpu_d24_arm:
+  case fpu_d25_arm:
+  case fpu_d26_arm:
+  case fpu_d27_arm:
+  case fpu_d28_arm:
+  case fpu_d29_arm:
+  case fpu_d30_arm:
+  case fpu_d31_arm:
+    tls_context.D[reg - fpu_d0_arm] = reg_value.GetAsUInt64();
+    break;
+
+  case fpu_q0_arm:
+  case fpu_q1_arm:
+  case fpu_q2_arm:
+  case fpu_q3_arm:
+  case fpu_q4_arm:
+  case fpu_q5_arm:
+  case fpu_q6_arm:
+  case fpu_q7_arm:
+  case fpu_q8_arm:
+  case fpu_q9_arm:
+  case fpu_q10_arm:
+  case fpu_q11_arm:
+  case fpu_q12_arm:
+  case fpu_q13_arm:
+  case fpu_q14_arm:
+  case fpu_q15_arm:
+    memcpy(&tls_context.Q[reg - fpu_q0_arm], reg_value.GetBytes(), 16);
+    break;
+
+  case fpu_fpscr_arm:
+    tls_context.Fpscr = reg_value.GetAsUInt32();
+    break;
+  }
+
+  return SetThreadContextHelper(thread_handle, &tls_context);
+}
+
+Status
+NativeRegisterContextWindows_arm::ReadRegister(const RegisterInfo *reg_info,
+                                               RegisterValue &reg_value) {
+  Status error;
+  if (!reg_info) {
+    error.SetErrorString("reg_info NULL");
+    return error;
+  }
+
+  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+  if (reg == LLDB_INVALID_REGNUM) {
+    // This is likely an internal register for lldb use only and should not be
+    // directly queried.
+    error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
+                                   "register, cannot read directly",
+                                   reg_info->name);
+    return error;
+  }
+
+  if (IsGPR(reg))
+    return GPRRead(reg, reg_value);
+
+  if (IsFPR(reg))
+    return FPRRead(reg, reg_value);
+
+  return Status("unimplemented");
+}
+
+Status NativeRegisterContextWindows_arm::WriteRegister(
+    const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+  Status error;
+
+  if (!reg_info) {
+    error.SetErrorString("reg_info NULL");
+    return error;
+  }
+
+  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+  if (reg == LLDB_INVALID_REGNUM) {
+    // This is likely an internal register for lldb use only and should not be
+    // directly written.
+    error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
+                                   "register, cannot write directly",
+                                   reg_info->name);
+    return error;
+  }
+
+  if (IsGPR(reg))
+    return GPRWrite(reg, reg_value);
+
+  if (IsFPR(reg))
+    return FPRWrite(reg, reg_value);
+
+  return Status("unimplemented");
+}
+
+Status NativeRegisterContextWindows_arm::ReadAllRegisterValues(
+    lldb::DataBufferSP &data_sp) {
+  const size_t data_size = REG_CONTEXT_SIZE;
+  data_sp = std::make_shared<DataBufferHeap>(data_size, 0);
+  ::CONTEXT tls_context;
+  Status error =
+      GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_ALL);
+  if (error.Fail())
+    return error;
+
+  uint8_t *dst = data_sp->GetBytes();
+  ::memcpy(dst, &tls_context, data_size);
+  return error;
+}
+
+Status NativeRegisterContextWindows_arm::WriteAllRegisterValues(
+    const lldb::DataBufferSP &data_sp) {
+  Status error;
+  const size_t data_size = REG_CONTEXT_SIZE;
+  if (!data_sp) {
+    error.SetErrorStringWithFormat(
+        "NativeRegisterContextWindows_arm::%s invalid data_sp provided",
+        __FUNCTION__);
+    return error;
+  }
+
+  if (data_sp->GetByteSize() != data_size) {
+    error.SetErrorStringWithFormatv(
+        "data_sp contained mismatched data size, expected {0}, actual {1}",
+        data_size, data_sp->GetByteSize());
+    return error;
+  }
+
+  ::CONTEXT tls_context;
+  memcpy(&tls_context, data_sp->GetBytes(), data_size);
+  return SetThreadContextHelper(GetThreadHandle(), &tls_context);
+}
+
+Status NativeRegisterContextWindows_arm::IsWatchpointHit(uint32_t wp_index,
+                                                         bool &is_hit) {
+  return Status("unimplemented");
+}
+
+Status NativeRegisterContextWindows_arm::GetWatchpointHitIndex(
+    uint32_t &wp_index, lldb::addr_t trap_addr) {
+  return Status("unimplemented");
+}
+
+Status NativeRegisterContextWindows_arm::IsWatchpointVacant(uint32_t wp_index,
+                                                            bool &is_vacant) {
+  return Status("unimplemented");
+}
+
+Status NativeRegisterContextWindows_arm::SetHardwareWatchpointWithIndex(
+    lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
+  return Status("unimplemented");
+}
+
+bool NativeRegisterContextWindows_arm::ClearHardwareWatchpoint(
+    uint32_t wp_index) {
+  return false;
+}
+
+Status NativeRegisterContextWindows_arm::ClearAllHardwareWatchpoints() {
+  return Status("unimplemented");
+}
+
+uint32_t NativeRegisterContextWindows_arm::SetHardwareWatchpoint(
+    lldb::addr_t addr, size_t size, uint32_t watch_flags) {
+  return LLDB_INVALID_INDEX32;
+}
+
+lldb::addr_t
+NativeRegisterContextWindows_arm::GetWatchpointAddress(uint32_t wp_index) {
+  return LLDB_INVALID_ADDRESS;
+}
+
+uint32_t NativeRegisterContextWindows_arm::NumSupportedHardwareWatchpoints() {
+  // Not implemented
+  return 0;
+}
+
+#endif // defined(__arm__) || defined(_M_ARM)

diff  --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h
new file mode 100644
index 000000000000..2778bed9a78d
--- /dev/null
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h
@@ -0,0 +1,80 @@
+//===-- NativeRegisterContextWindows_arm.h ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__arm__) || defined(_M_ARM)
+#ifndef liblldb_NativeRegisterContextWindows_arm_h_
+#define liblldb_NativeRegisterContextWindows_arm_h_
+
+#include "Plugins/Process/Utility/lldb-arm-register-enums.h"
+
+#include "NativeRegisterContextWindows.h"
+
+namespace lldb_private {
+
+class NativeThreadWindows;
+
+class NativeRegisterContextWindows_arm : public NativeRegisterContextWindows {
+public:
+  NativeRegisterContextWindows_arm(const ArchSpec &target_arch,
+                                   NativeThreadProtocol &native_thread);
+
+  uint32_t GetRegisterSetCount() const override;
+
+  const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
+
+  Status ReadRegister(const RegisterInfo *reg_info,
+                      RegisterValue &reg_value) override;
+
+  Status WriteRegister(const RegisterInfo *reg_info,
+                       const RegisterValue &reg_value) override;
+
+  Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+  Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+  Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
+
+  Status GetWatchpointHitIndex(uint32_t &wp_index,
+                               lldb::addr_t trap_addr) override;
+
+  Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
+
+  bool ClearHardwareWatchpoint(uint32_t wp_index) override;
+
+  Status ClearAllHardwareWatchpoints() override;
+
+  Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
+                                        uint32_t watch_flags,
+                                        uint32_t wp_index);
+
+  uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
+                                 uint32_t watch_flags) override;
+
+  lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override;
+
+  uint32_t NumSupportedHardwareWatchpoints() override;
+
+protected:
+  Status GPRRead(const uint32_t reg, RegisterValue &reg_value);
+
+  Status GPRWrite(const uint32_t reg, const RegisterValue &reg_value);
+
+  Status FPRRead(const uint32_t reg, RegisterValue &reg_value);
+
+  Status FPRWrite(const uint32_t reg, const RegisterValue &reg_value);
+
+private:
+  bool IsGPR(uint32_t reg_index) const;
+
+  bool IsFPR(uint32_t reg_index) const;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_NativeRegisterContextWindows_arm_h_
+#endif // defined(__arm__) || defined(_M_ARM)

diff  --git a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
index 416abed88230..86a302a87b86 100644
--- a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
@@ -20,13 +20,14 @@
 #include "ProcessWindowsLog.h"
 #include "TargetThreadWindows.h"
 
-// TODO support _M_ARM
 #if defined(__x86_64__) || defined(_M_AMD64)
 #include "x64/RegisterContextWindows_x64.h"
 #elif defined(__i386__) || defined(_M_IX86)
 #include "x86/RegisterContextWindows_x86.h"
 #elif defined(__aarch64__) || defined(_M_ARM64)
 #include "arm64/RegisterContextWindows_arm64.h"
+#elif defined(__arm__) || defined(_M_ARM)
+#include "arm/RegisterContextWindows_arm.h"
 #endif
 
 using namespace lldb;
@@ -71,7 +72,12 @@ TargetThreadWindows::CreateRegisterContextForFrame(StackFrame *frame) {
       switch (arch.GetMachine()) {
       case llvm::Triple::arm:
       case llvm::Triple::thumb:
-        LLDB_LOG(log, "debugging ARM (NT) targets is currently unsupported");
+#if defined(__arm__) || defined(_M_ARM)
+        m_thread_reg_ctx_sp.reset(
+            new RegisterContextWindows_arm(*this, concrete_frame_idx));
+#else
+        LLDB_LOG(log, "debugging foreign targets is currently unsupported");
+#endif
         break;
 
       case llvm::Triple::aarch64:

diff  --git a/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp b/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp
new file mode 100644
index 000000000000..c8bcf71c7f6d
--- /dev/null
+++ b/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp
@@ -0,0 +1,432 @@
+//===-- RegisterContextWindows_arm.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__arm__) || defined(_M_ARM)
+
+#include "lldb/Host/windows/HostThreadWindows.h"
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private-types.h"
+
+#include "RegisterContextWindows_arm.h"
+#include "TargetThreadWindows.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define GPR_OFFSET(idx) 0
+#define FPU_OFFSET(idx) 0
+#define FPSCR_OFFSET 0
+#define EXC_OFFSET(reg) 0
+#define DBG_OFFSET_NAME(reg) 0
+
+#define DEFINE_DBG(reg, i)                                                     \
+  #reg, NULL,                                                                  \
+      0, DBG_OFFSET_NAME(reg[i]), eEncodingUint, eFormatHex,                   \
+                              {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,       \
+                               LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,       \
+                               LLDB_INVALID_REGNUM },                          \
+                               NULL, NULL, NULL, 0
+
+// Include RegisterInfos_arm to declare our g_register_infos_arm structure.
+#define DECLARE_REGISTER_INFOS_ARM_STRUCT
+#include "Plugins/Process/Utility/RegisterInfos_arm.h"
+#undef DECLARE_REGISTER_INFOS_ARM_STRUCT
+
+static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos_arm);
+
+// Array of lldb register numbers used to define the set of all General Purpose
+// Registers
+uint32_t g_gpr_reg_indices[] = {
+    gpr_r0, gpr_r1,  gpr_r2,  gpr_r3,  gpr_r4, gpr_r5, gpr_r6, gpr_r7,   gpr_r8,
+    gpr_r9, gpr_r10, gpr_r11, gpr_r12, gpr_sp, gpr_lr, gpr_pc, gpr_cpsr,
+};
+
+uint32_t g_fpu_reg_indices[] = {
+    fpu_s0,    fpu_s1,  fpu_s2,  fpu_s3,  fpu_s4,  fpu_s5,  fpu_s6,  fpu_s7,
+    fpu_s8,    fpu_s9,  fpu_s10, fpu_s11, fpu_s12, fpu_s13, fpu_s14, fpu_s15,
+    fpu_s16,   fpu_s17, fpu_s18, fpu_s19, fpu_s20, fpu_s21, fpu_s22, fpu_s23,
+    fpu_s24,   fpu_s25, fpu_s26, fpu_s27, fpu_s28, fpu_s29, fpu_s30, fpu_s31,
+
+    fpu_d0,    fpu_d1,  fpu_d2,  fpu_d3,  fpu_d4,  fpu_d5,  fpu_d6,  fpu_d7,
+    fpu_d8,    fpu_d9,  fpu_d10, fpu_d11, fpu_d12, fpu_d13, fpu_d14, fpu_d15,
+    fpu_d16,   fpu_d17, fpu_d18, fpu_d19, fpu_d20, fpu_d21, fpu_d22, fpu_d23,
+    fpu_d24,   fpu_d25, fpu_d26, fpu_d27, fpu_d28, fpu_d29, fpu_d30, fpu_d31,
+
+    fpu_q0,    fpu_q1,  fpu_q2,  fpu_q3,  fpu_q4,  fpu_q5,  fpu_q6,  fpu_q7,
+    fpu_q8,    fpu_q9,  fpu_q10, fpu_q11, fpu_q12, fpu_q13, fpu_q14, fpu_q15,
+
+    fpu_fpscr,
+};
+
+RegisterSet g_register_sets[] = {
+    {"General Purpose Registers", "gpr",
+     llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices},
+    {"Floating Point Registers", "fpu", llvm::array_lengthof(g_fpu_reg_indices),
+     g_fpu_reg_indices},
+};
+
+// Constructors and Destructors
+RegisterContextWindows_arm::RegisterContextWindows_arm(
+    Thread &thread, uint32_t concrete_frame_idx)
+    : RegisterContextWindows(thread, concrete_frame_idx) {}
+
+RegisterContextWindows_arm::~RegisterContextWindows_arm() {}
+
+size_t RegisterContextWindows_arm::GetRegisterCount() {
+  return llvm::array_lengthof(g_register_infos_arm);
+}
+
+const RegisterInfo *
+RegisterContextWindows_arm::GetRegisterInfoAtIndex(size_t reg) {
+  if (reg < k_num_register_infos)
+    return &g_register_infos_arm[reg];
+  return NULL;
+}
+
+size_t RegisterContextWindows_arm::GetRegisterSetCount() {
+  return llvm::array_lengthof(g_register_sets);
+}
+
+const RegisterSet *RegisterContextWindows_arm::GetRegisterSet(size_t reg_set) {
+  return &g_register_sets[reg_set];
+}
+
+bool RegisterContextWindows_arm::ReadRegister(const RegisterInfo *reg_info,
+                                              RegisterValue &reg_value) {
+  if (!CacheAllRegisterValues())
+    return false;
+
+  if (reg_info == nullptr)
+    return false;
+
+  const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+  switch (reg) {
+  case gpr_r0:
+    reg_value.SetUInt32(m_context.R0);
+    break;
+  case gpr_r1:
+    reg_value.SetUInt32(m_context.R1);
+    break;
+  case gpr_r2:
+    reg_value.SetUInt32(m_context.R2);
+    break;
+  case gpr_r3:
+    reg_value.SetUInt32(m_context.R3);
+    break;
+  case gpr_r4:
+    reg_value.SetUInt32(m_context.R4);
+    break;
+  case gpr_r5:
+    reg_value.SetUInt32(m_context.R5);
+    break;
+  case gpr_r6:
+    reg_value.SetUInt32(m_context.R6);
+    break;
+  case gpr_r7:
+    reg_value.SetUInt32(m_context.R7);
+    break;
+  case gpr_r8:
+    reg_value.SetUInt32(m_context.R8);
+    break;
+  case gpr_r9:
+    reg_value.SetUInt32(m_context.R9);
+    break;
+  case gpr_r10:
+    reg_value.SetUInt32(m_context.R10);
+    break;
+  case gpr_r11:
+    reg_value.SetUInt32(m_context.R11);
+    break;
+  case gpr_r12:
+    reg_value.SetUInt32(m_context.R12);
+    break;
+  case gpr_sp:
+    reg_value.SetUInt32(m_context.Sp);
+    break;
+  case gpr_lr:
+    reg_value.SetUInt32(m_context.Lr);
+    break;
+  case gpr_pc:
+    reg_value.SetUInt32(m_context.Pc);
+    break;
+  case gpr_cpsr:
+    reg_value.SetUInt32(m_context.Cpsr);
+    break;
+
+  case fpu_s0:
+  case fpu_s1:
+  case fpu_s2:
+  case fpu_s3:
+  case fpu_s4:
+  case fpu_s5:
+  case fpu_s6:
+  case fpu_s7:
+  case fpu_s8:
+  case fpu_s9:
+  case fpu_s10:
+  case fpu_s11:
+  case fpu_s12:
+  case fpu_s13:
+  case fpu_s14:
+  case fpu_s15:
+  case fpu_s16:
+  case fpu_s17:
+  case fpu_s18:
+  case fpu_s19:
+  case fpu_s20:
+  case fpu_s21:
+  case fpu_s22:
+  case fpu_s23:
+  case fpu_s24:
+  case fpu_s25:
+  case fpu_s26:
+  case fpu_s27:
+  case fpu_s28:
+  case fpu_s29:
+  case fpu_s30:
+  case fpu_s31:
+    reg_value.SetUInt32(m_context.S[reg - fpu_s0], RegisterValue::eTypeFloat);
+    break;
+
+  case fpu_d0:
+  case fpu_d1:
+  case fpu_d2:
+  case fpu_d3:
+  case fpu_d4:
+  case fpu_d5:
+  case fpu_d6:
+  case fpu_d7:
+  case fpu_d8:
+  case fpu_d9:
+  case fpu_d10:
+  case fpu_d11:
+  case fpu_d12:
+  case fpu_d13:
+  case fpu_d14:
+  case fpu_d15:
+  case fpu_d16:
+  case fpu_d17:
+  case fpu_d18:
+  case fpu_d19:
+  case fpu_d20:
+  case fpu_d21:
+  case fpu_d22:
+  case fpu_d23:
+  case fpu_d24:
+  case fpu_d25:
+  case fpu_d26:
+  case fpu_d27:
+  case fpu_d28:
+  case fpu_d29:
+  case fpu_d30:
+  case fpu_d31:
+    reg_value.SetUInt64(m_context.D[reg - fpu_d0], RegisterValue::eTypeDouble);
+    break;
+
+  case fpu_q0:
+  case fpu_q1:
+  case fpu_q2:
+  case fpu_q3:
+  case fpu_q4:
+  case fpu_q5:
+  case fpu_q6:
+  case fpu_q7:
+  case fpu_q8:
+  case fpu_q9:
+  case fpu_q10:
+  case fpu_q11:
+  case fpu_q12:
+  case fpu_q13:
+  case fpu_q14:
+  case fpu_q15:
+    reg_value.SetBytes(&m_context.Q[reg - fpu_q0], reg_info->byte_size,
+                       endian::InlHostByteOrder());
+    break;
+
+  case fpu_fpscr:
+    reg_value.SetUInt32(m_context.Fpscr);
+    break;
+
+  default:
+    reg_value.SetValueToInvalid();
+    return false;
+  }
+  return true;
+}
+
+bool RegisterContextWindows_arm::WriteRegister(const RegisterInfo *reg_info,
+                                               const RegisterValue &reg_value) {
+  // Since we cannot only write a single register value to the inferior, we
+  // need to make sure our cached copy of the register values are fresh.
+  // Otherwise when writing EAX, for example, we may also overwrite some other
+  // register with a stale value.
+  if (!CacheAllRegisterValues())
+    return false;
+
+  const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+  switch (reg) {
+  case gpr_r0:
+    m_context.R0 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r1:
+    m_context.R1 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r2:
+    m_context.R2 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r3:
+    m_context.R3 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r4:
+    m_context.R4 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r5:
+    m_context.R5 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r6:
+    m_context.R6 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r7:
+    m_context.R7 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r8:
+    m_context.R8 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r9:
+    m_context.R9 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r10:
+    m_context.R10 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r11:
+    m_context.R11 = reg_value.GetAsUInt32();
+    break;
+  case gpr_r12:
+    m_context.R12 = reg_value.GetAsUInt32();
+    break;
+  case gpr_sp:
+    m_context.Sp = reg_value.GetAsUInt32();
+    break;
+  case gpr_lr:
+    m_context.Lr = reg_value.GetAsUInt32();
+    break;
+  case gpr_pc:
+    m_context.Pc = reg_value.GetAsUInt32();
+    break;
+  case gpr_cpsr:
+    m_context.Cpsr = reg_value.GetAsUInt32();
+    break;
+
+  case fpu_s0:
+  case fpu_s1:
+  case fpu_s2:
+  case fpu_s3:
+  case fpu_s4:
+  case fpu_s5:
+  case fpu_s6:
+  case fpu_s7:
+  case fpu_s8:
+  case fpu_s9:
+  case fpu_s10:
+  case fpu_s11:
+  case fpu_s12:
+  case fpu_s13:
+  case fpu_s14:
+  case fpu_s15:
+  case fpu_s16:
+  case fpu_s17:
+  case fpu_s18:
+  case fpu_s19:
+  case fpu_s20:
+  case fpu_s21:
+  case fpu_s22:
+  case fpu_s23:
+  case fpu_s24:
+  case fpu_s25:
+  case fpu_s26:
+  case fpu_s27:
+  case fpu_s28:
+  case fpu_s29:
+  case fpu_s30:
+  case fpu_s31:
+    m_context.S[reg - fpu_s0] = reg_value.GetAsUInt32();
+    break;
+
+  case fpu_d0:
+  case fpu_d1:
+  case fpu_d2:
+  case fpu_d3:
+  case fpu_d4:
+  case fpu_d5:
+  case fpu_d6:
+  case fpu_d7:
+  case fpu_d8:
+  case fpu_d9:
+  case fpu_d10:
+  case fpu_d11:
+  case fpu_d12:
+  case fpu_d13:
+  case fpu_d14:
+  case fpu_d15:
+  case fpu_d16:
+  case fpu_d17:
+  case fpu_d18:
+  case fpu_d19:
+  case fpu_d20:
+  case fpu_d21:
+  case fpu_d22:
+  case fpu_d23:
+  case fpu_d24:
+  case fpu_d25:
+  case fpu_d26:
+  case fpu_d27:
+  case fpu_d28:
+  case fpu_d29:
+  case fpu_d30:
+  case fpu_d31:
+    m_context.D[reg - fpu_d0] = reg_value.GetAsUInt64();
+    break;
+
+  case fpu_q0:
+  case fpu_q1:
+  case fpu_q2:
+  case fpu_q3:
+  case fpu_q4:
+  case fpu_q5:
+  case fpu_q6:
+  case fpu_q7:
+  case fpu_q8:
+  case fpu_q9:
+  case fpu_q10:
+  case fpu_q11:
+  case fpu_q12:
+  case fpu_q13:
+  case fpu_q14:
+  case fpu_q15:
+    memcpy(&m_context.Q[reg - fpu_q0], reg_value.GetBytes(), 16);
+    break;
+
+  case fpu_fpscr:
+    m_context.Fpscr = reg_value.GetAsUInt32();
+    break;
+
+  default:
+    return false;
+  }
+
+  // Physically update the registers in the target process.
+  return ApplyAllRegisterValues();
+}
+
+#endif // defined(__arm__) || defined(_M_ARM)

diff  --git a/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h b/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h
new file mode 100644
index 000000000000..57050671d40c
--- /dev/null
+++ b/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h
@@ -0,0 +1,47 @@
+//===-- RegisterContextWindows_arm.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextWindows_arm_H_
+#define liblldb_RegisterContextWindows_arm_H_
+
+#if defined(__arm__) || defined(_M_ARM)
+
+#include "RegisterContextWindows.h"
+#include "lldb/lldb-forward.h"
+
+namespace lldb_private {
+
+class Thread;
+
+class RegisterContextWindows_arm : public RegisterContextWindows {
+public:
+  // Constructors and Destructors
+  RegisterContextWindows_arm(Thread &thread, uint32_t concrete_frame_idx);
+
+  virtual ~RegisterContextWindows_arm();
+
+  // Subclasses must override these functions
+  size_t GetRegisterCount() override;
+
+  const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+  size_t GetRegisterSetCount() override;
+
+  const RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+  bool ReadRegister(const RegisterInfo *reg_info,
+                    RegisterValue &reg_value) override;
+
+  bool WriteRegister(const RegisterInfo *reg_info,
+                     const RegisterValue &reg_value) override;
+};
+} // namespace lldb_private
+
+#endif // defined(__arm__) || defined(_M_ARM)
+
+#endif // #ifndef liblldb_RegisterContextWindows_arm_H_

diff  --git a/lldb/test/Shell/Register/Inputs/arm-fp-read.cpp b/lldb/test/Shell/Register/Inputs/arm-fp-read.cpp
new file mode 100644
index 000000000000..2dce2ed2d753
--- /dev/null
+++ b/lldb/test/Shell/Register/Inputs/arm-fp-read.cpp
@@ -0,0 +1,19 @@
+int main() {
+  asm volatile(
+    "vmov.f64 d0,  #0.5\n\t"
+    "vmov.f64 d1,  #1.5\n\t"
+    "vmov.f64 d2,  #2.5\n\t"
+    "vmov.f64 d3,  #3.5\n\t"
+    "vmov.f32 s8,  #4.5\n\t"
+    "vmov.f32 s9,  #5.5\n\t"
+    "vmov.f32 s10, #6.5\n\t"
+    "vmov.f32 s11, #7.5\n\t"
+    "\n\t"
+    "bkpt     #0\n\t"
+    :
+    :
+    : "d0", "d1", "d2", "d3", "s8", "s9", "s10", "s11"
+  );
+
+  return 0;
+}

diff  --git a/lldb/test/Shell/Register/Inputs/arm-gp-read.cpp b/lldb/test/Shell/Register/Inputs/arm-gp-read.cpp
new file mode 100644
index 000000000000..fd891c49c09a
--- /dev/null
+++ b/lldb/test/Shell/Register/Inputs/arm-gp-read.cpp
@@ -0,0 +1,44 @@
+#include <cstdint>
+
+struct alignas(16) vec_t {
+  uint64_t a, b;
+};
+
+int main() {
+  constexpr uint32_t gprs[] = {
+    0x00010203,
+    0x10111213,
+    0x20212223,
+    0x30313233,
+    0x40414243,
+    0x50515253,
+    0x60616263,
+    0x70717273,
+  };
+
+  constexpr vec_t vecs[] = {
+    { 0x0F0E0D0C0B0A0908, 0x1716151413121110, },
+    { 0x100F0E0D0C0B0A09, 0x1817161514131211, },
+    { 0x11100F0E0D0C0B0A, 0x1918171615141312, },
+    { 0x1211100F0E0D0C0B, 0x1A19181716151413, },
+  };
+  const vec_t *vec_ptr = vecs;
+
+  asm volatile(
+    "ldrd     r0,  r1,  [%1]\n\t"
+    "ldrd     r2,  r3,  [%1, #8]\n\t"
+    "ldrd     r4,  r5,  [%1, #16]\n\t"
+    "ldrd     r6,  r7,  [%1, #24]\n\t"
+    "\n\t"
+    "vld1.64  {q0, q1}, [%0]!\n\t"
+    "vld1.64  {q2, q3}, [%0]!\n\t"
+    "\n\t"
+    "bkpt     #0\n\t"
+    : "+r"(vec_ptr)
+    : "r"(gprs)
+    : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+      "q0", "q1", "q2", "q3"
+  );
+
+  return 0;
+}

diff  --git a/lldb/test/Shell/Register/arm-fp-read.test b/lldb/test/Shell/Register/arm-fp-read.test
new file mode 100644
index 000000000000..21af9074e3b3
--- /dev/null
+++ b/lldb/test/Shell/Register/arm-fp-read.test
@@ -0,0 +1,21 @@
+# REQUIRES: native && target-arm
+# RUN: %clangxx -fomit-frame-pointer %p/Inputs/arm-fp-read.cpp -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+process launch
+
+register read d0
+register read d1
+register read d2
+register read d3
+register read s8
+register read s9
+register read s10
+register read s11
+# CHECK-DAG: d0 = 0.5
+# CHECK-DAG: d1 = 1.5
+# CHECK-DAG: d2 = 2.5
+# CHECK-DAG: d3 = 3.5
+# CHECK-DAG: s8 = 4.5
+# CHECK-DAG: s9 = 5.5
+# CHECK-DAG: s10 = 6.5
+# CHECK-DAG: s11 = 7.5

diff  --git a/lldb/test/Shell/Register/arm-gp-read.test b/lldb/test/Shell/Register/arm-gp-read.test
new file mode 100644
index 000000000000..73c1034b6e23
--- /dev/null
+++ b/lldb/test/Shell/Register/arm-gp-read.test
@@ -0,0 +1,19 @@
+# REQUIRES: native && target-arm
+# RUN: %clangxx -fomit-frame-pointer %p/Inputs/arm-gp-read.cpp -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+process launch
+
+register read --all
+# CHECK-DAG: r0 = 0x00010203
+# CHECK-DAG: r1 = 0x10111213
+# CHECK-DAG: r2 = 0x20212223
+# CHECK-DAG: r3 = 0x30313233
+# CHECK-DAG: r4 = 0x40414243
+# CHECK-DAG: r5 = 0x50515253
+# CHECK-DAG: r6 = 0x60616263
+# CHECK-DAG: r7 = 0x70717273
+
+# CHECK-DAG: q0 = {0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17}
+# CHECK-DAG: q1 = {0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18}
+# CHECK-DAG: q2 = {0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19}
+# CHECK-DAG: q3 = {0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a}

diff  --git a/llvm/utils/lit/lit/llvm/config.py b/llvm/utils/lit/lit/llvm/config.py
index 5b13e8de9603..b0432995df14 100644
--- a/llvm/utils/lit/lit/llvm/config.py
+++ b/llvm/utils/lit/lit/llvm/config.py
@@ -99,6 +99,8 @@ def __init__(self, lit_config, config):
                 features.add('target-x86_64')
             elif re.match(r'^aarch64.*', target_triple):
                 features.add('target-aarch64')
+            elif re.match(r'^arm.*', target_triple):
+                features.add('target-arm')
 
         use_gmalloc = lit_config.params.get('use_gmalloc', None)
         if lit.util.pythonize_bool(use_gmalloc):


        


More information about the lldb-commits mailing list