https://github.com/DavidSpickett updated 
https://github.com/llvm/llvm-project/pull/209483

>From 765ae437a29c80143744a53632e8bf339710f586 Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Mon, 13 Jul 2026 15:46:52 +0000
Subject: [PATCH 1/2] [lldb][AArch64][NFC] Make watchpoint adjustment more
 clear

NativeRegisterContextDBReg_arm64::AdjustWatchpoint has a way of
adjusting the range of a watchpoint to fit hardware expectations
but it took me a while to figure out how it worked.

I've rewritten it in a way I think is easier to follow, with
more explanatory comments.
---
 .../NativeRegisterContextDBReg_arm64.cpp      | 65 +++++++++++++------
 1 file changed, 46 insertions(+), 19 deletions(-)

diff --git 
a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp 
b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
index f2f772ad615c9..fff11c47c1395 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
@@ -12,6 +12,8 @@
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/RegisterValue.h"
 
+#include "llvm/Support/MathExtras.h"
+
 using namespace lldb_private;
 
 uint32_t
@@ -36,30 +38,55 @@ 
NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
 std::optional<NativeRegisterContextDBReg::WatchpointDetails>
 NativeRegisterContextDBReg_arm64::AdjustWatchpoint(
     const WatchpointDetails &details) {
+  // A BAS watchpoint's size must be a power of 2 that is no greater than 8.
+  const size_t max_size = 8;
   size_t size = details.size;
-  lldb::addr_t addr = details.addr;
-  // Check if size has a valid hardware watchpoint length.
-  if (size != 1 && size != 2 && size != 4 && size != 8)
+  if (!llvm::isPowerOf2_64(size) || size > max_size)
     return std::nullopt;
 
-  // Check 8-byte alignment for hardware watchpoint target address. Below is a
-  // hack to recalculate address and size in order to make sure we can watch
-  // non 8-byte aligned addresses as well.
-  if (addr & 0x07) {
-    uint8_t watch_mask = (addr & 0x07) + size;
-
-    if (watch_mask > 0x08)
-      return std::nullopt;
+  // The start address must be aligned to 8 bytes.
+  lldb::addr_t addr = details.addr;
+  const size_t misalignment = addr & (max_size - 1);
+  if (misalignment == 0)
+    return details;
+
+  // The start address is not aligned, but we might be able to expand the
+  // watched range backwards to the previous 8 byte aligned address while
+  // keeping the size <= 8 bytes.
+  //
+  //  Aligned Address
+  //  |     /--------- Start Address
+  // [ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
+  //  |     |<-------- Size ------->|
+  //  |<----- Aligned Size -------->|
+  //
+  size_t aligned_size = misalignment + size;
+  if (aligned_size > max_size) {
+    // The range does not fit within a single BAS watchpoint, reject it.
+    return std::nullopt;
+  }
 
-    if (watch_mask <= 0x02)
-      size = 2;
-    else if (watch_mask <= 0x04)
-      size = 4;
-    else
-      size = 8;
+  // Size must still be a power of 2. After correcting this, sometimes we will
+  // watch bytes before and after the intended range. Stops in these extra 
bytes
+  // will be filtered out.
+  //
+  // For example, A is an aligned address and S is the start address. You want
+  // to watch the 1 byte at address S.
+  // [A][ ][S][ ]
+  //       { }
+  // This is misaligned by 2, so our aligned size is 3.
+  // [A][ ][S][ ]
+  // {       }
+  // We cannot watch 3 bytes but we can watch 4.
+  // [A][ ][S][ ]
+  // {          }
+  // The watched range is expanded before and after S.
+
+  // Round up to a power of 2 size.
+  size = llvm::PowerOf2Ceil(aligned_size);
+  // Align the address down.
+  addr -= misalignment;
 
-    addr = addr & (~0x07);
-  }
   return WatchpointDetails{size, addr};
 }
 

>From 103ae09e5ce360e846bae0e360c8028535758d7d Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Wed, 15 Jul 2026 12:47:33 +0000
Subject: [PATCH 2/2] update comments

---
 .../Utility/NativeRegisterContextDBReg_arm64.cpp    | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git 
a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp 
b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
index fff11c47c1395..91c440f4c0453 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
@@ -38,7 +38,8 @@ NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t 
wp_index) {
 std::optional<NativeRegisterContextDBReg::WatchpointDetails>
 NativeRegisterContextDBReg_arm64::AdjustWatchpoint(
     const WatchpointDetails &details) {
-  // A BAS watchpoint's size must be a power of 2 that is no greater than 8.
+  // For the way we are using BAS watchpoints, their size must be a power of 2
+  // that is less than 8. Note that this is a subset of what hardware allows.
   const size_t max_size = 8;
   size_t size = details.size;
   if (!llvm::isPowerOf2_64(size) || size > max_size)
@@ -94,14 +95,15 @@ uint32_t 
NativeRegisterContextDBReg_arm64::MakeBreakControlValue(size_t size) {
   // PAC (bits 2:1): 0b10
   const uint32_t pac_bits = 2 << 1;
 
-  // BAS (bits 12:5) hold a bit-mask of addresses to watch
+  // BAS (bits 12:5) hold a bit-mask of addresses to watch.
+  // Hardware does allow gaps but we only support unbroken ranges at this time
+  //
   // e.g. 0b00000001 means 1 byte at address
   //      0b00000011 means 2 bytes (addr..addr+1)
   //      ...
   //      0b11111111 means 8 bytes (addr..addr+7)
   size_t encoded_size = ((1 << size) - 1) << 5;
 
-  // Return encoded hardware breakpoint control value.
   return m_hw_dbg_enable_bit | pac_bits | encoded_size;
 }
 
@@ -111,13 +113,14 @@ 
NativeRegisterContextDBReg_arm64::MakeWatchControlValue(size_t size,
   // PAC (bits 2:1): 0b10
   const uint32_t pac_bits = 2 << 1;
 
-  // BAS (bits 12:5) hold a bit-mask of addresses to watch
+  // BAS (bits 12:5) hold a bit-mask of addresses to watch.
+  // Hardware does allow gaps but we only support unbroken ranges at this time.
+  //
   // e.g. 0b00000001 means 1 byte at address
   //      0b00000011 means 2 bytes (addr..addr+1)
   //      ...
   //      0b11111111 means 8 bytes (addr..addr+7)
   size_t encoded_size = ((1 << size) - 1) << 5;
 
-  // Return encoded hardware watchpoint control value.
   return m_hw_dbg_enable_bit | pac_bits | encoded_size | (watch_flags << 3);
 }

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to