[Lldb-commits] [lldb] [lldb] Add pointer arithmetics for addition and subtraction to DIL (PR #184652)

Ilia Kuklin via lldb-commits lldb-commits at lists.llvm.org
Tue Mar 10 04:12:10 PDT 2026


https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/184652

>From 3be758d91032525ee5e82c8034db7116274e3f5c Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 4 Mar 2026 19:46:38 +0500
Subject: [PATCH] [lldb] Add pointer arithmetics for addition and subtraction
 to DIL

---
 lldb/include/lldb/ValueObject/DILEval.h       |   4 +
 lldb/source/ValueObject/DILEval.cpp           | 110 +++++++++++++++--
 .../Arithmetic/TestFrameVarDILArithmetic.py   |  12 --
 .../TestFrameVarDILExprPointerArithmetic.py   | 113 ++++++++++++++++++
 .../var-dil/expr/PointerArithmetic/main.cpp   |  19 +++
 5 files changed, 239 insertions(+), 19 deletions(-)

diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index a6223c4d998ab..f4506bf626ab4 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -89,6 +89,10 @@ class Interpreter : Visitor {
   llvm::Expected<CompilerType> ArithmeticConversion(lldb::ValueObjectSP &lhs,
                                                     lldb::ValueObjectSP &rhs,
                                                     uint32_t location);
+  /// Add the offset to the pointer according to the pointee type byte size.
+  /// \returns A new `ValueObject` with a new pointer value.
+  llvm::Expected<lldb::ValueObjectSP>
+  PointerAdd(lldb::ValueObjectSP ptr, int64_t offset, uint32_t location);
   llvm::Expected<lldb::ValueObjectSP> EvaluateScalarOp(BinaryOpKind kind,
                                                        lldb::ValueObjectSP lhs,
                                                        lldb::ValueObjectSP rhs,
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 1b841d39a034e..9375c5f30834d 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -538,6 +538,29 @@ Interpreter::Visit(const UnaryOpNode &node) {
                                               node.GetLocation());
 }
 
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::PointerAdd(lldb::ValueObjectSP ptr, int64_t offset,
+                        uint32_t location) {
+  if (ptr->GetCompilerType().IsPointerToVoid())
+    return llvm::make_error<DILDiagnosticError>(
+        m_expr, "arithmetic on a pointer to void", location);
+  if (ptr->GetValueAsUnsigned(0) == 0 && offset != 0)
+    return llvm::make_error<DILDiagnosticError>(
+        m_expr, "arithmetic on a nullptr is undefined", location);
+
+  llvm::Expected<uint64_t> byte_size =
+      ptr->GetCompilerType().GetPointeeType().GetByteSize(
+          m_exe_ctx_scope.get());
+  if (!byte_size)
+    return byte_size.takeError();
+  uintptr_t addr = ptr->GetValueAsUnsigned(0) + offset * (*byte_size);
+
+  ExecutionContext exe_ctx(m_target.get(), false);
+  Scalar scalar(addr);
+  return ValueObject::CreateValueObjectFromScalar(
+      m_target, scalar, ptr->GetCompilerType(), "result");
+}
+
 llvm::Expected<lldb::ValueObjectSP>
 Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs,
                               lldb::ValueObjectSP rhs, CompilerType result_type,
@@ -569,7 +592,8 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition(
     lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
   // Operation '+' works for:
   //   {scalar,unscoped_enum} <-> {scalar,unscoped_enum}
-  // TODO: Pointer arithmetics
+  //   {integer,unscoped_enum} <-> pointer
+  //   pointer <-> {integer,unscoped_enum}
   auto orig_lhs_type = lhs->GetCompilerType();
   auto orig_rhs_type = rhs->GetCompilerType();
   auto type_or_err = ArithmeticConversion(lhs, rhs, location);
@@ -580,18 +604,34 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition(
   if (result_type.IsScalarType())
     return EvaluateScalarOp(BinaryOpKind::Add, lhs, rhs, result_type, location);
 
-  std::string errMsg =
-      llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')",
-                    orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
-  return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
-                                              location);
+  // Check for pointer arithmetics.
+  // One of the operands must be a pointer and the other one an integer.
+  lldb::ValueObjectSP ptr, offset;
+  if (lhs->GetCompilerType().IsPointerType()) {
+    ptr = lhs;
+    offset = rhs;
+  } else if (rhs->GetCompilerType().IsPointerType()) {
+    ptr = rhs;
+    offset = lhs;
+  }
+
+  if (!ptr || !offset->GetCompilerType().IsInteger()) {
+    std::string errMsg =
+        llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')",
+                      orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
+    return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg),
+                                                location);
+  }
+
+  return PointerAdd(ptr, offset->GetValueAsUnsigned(0), location);
 }
 
 llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
     lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) {
   // Operation '-' works for:
   //   {scalar,unscoped_enum} <-> {scalar,unscoped_enum}
-  // TODO: Pointer arithmetics
+  //   pointer <-> {integer,unscoped_enum}
+  //   pointer <-> pointer (if pointee types are compatible)
   auto orig_lhs_type = lhs->GetCompilerType();
   auto orig_rhs_type = rhs->GetCompilerType();
   auto type_or_err = ArithmeticConversion(lhs, rhs, location);
@@ -602,6 +642,62 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction(
   if (result_type.IsScalarType())
     return EvaluateScalarOp(BinaryOpKind::Sub, lhs, rhs, result_type, location);
 
+  auto lhs_type = lhs->GetCompilerType();
+  auto rhs_type = rhs->GetCompilerType();
+
+  // "pointer - integer" operation.
+  if (lhs_type.IsPointerType() && rhs_type.IsInteger())
+    return PointerAdd(lhs, -rhs->GetValueAsUnsigned(0), location);
+
+  // "pointer - pointer" operation.
+  if (lhs_type.IsPointerType() && rhs_type.IsPointerType()) {
+    if (lhs_type.IsPointerToVoid() && rhs_type.IsPointerToVoid()) {
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, "arithmetic on pointers to void", location);
+    }
+    // Compare canonical unqualified pointer types.
+    CompilerType lhs_unqualified_type =
+        lhs_type.GetCanonicalType().GetFullyUnqualifiedType();
+    CompilerType rhs_unqualified_type =
+        rhs_type.GetCanonicalType().GetFullyUnqualifiedType();
+    bool comparable = lhs_unqualified_type.CompareTypes(rhs_unqualified_type);
+    if (!comparable) {
+      std::string errMsg = llvm::formatv(
+          "'{0}' and '{1}' are not pointers to compatible types",
+          orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
+      return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location);
+    }
+
+    llvm::Expected<uint64_t> lhs_byte_size =
+        lhs_type.GetPointeeType().GetByteSize(m_exe_ctx_scope.get());
+    if (!lhs_byte_size)
+      return lhs_byte_size.takeError();
+    // Since pointers have compatible types, both have the same pointee size.
+    int64_t item_size = *lhs_byte_size;
+    int64_t diff = static_cast<int64_t>(lhs->GetValueAsUnsigned(0) -
+                                        rhs->GetValueAsUnsigned(0));
+    if (diff % item_size != 0) {
+      // If address difference isn't divisible by pointee size then performing
+      // the operation is undefined behaviour.
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, "undefined pointer arithmetic", location);
+    }
+    diff /= item_size;
+
+    llvm::Expected<lldb::TypeSystemSP> type_system =
+        GetTypeSystemFromCU(m_exe_ctx_scope);
+    if (!type_system)
+      return type_system.takeError();
+    CompilerType ptrdiff_t = type_system.get()->GetPointerDiffType(true);
+    if (!ptrdiff_t)
+      return llvm::make_error<DILDiagnosticError>(
+          m_expr, "unable to determine pointer diff type", location);
+
+    Scalar scalar(diff);
+    return ValueObject::CreateValueObjectFromScalar(m_target, scalar, ptrdiff_t,
+                                                    "result");
+  }
+
   std::string errMsg =
       llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')",
                     orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName());
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
index 03075314ab9b6..fb3b877209d16 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py
@@ -102,15 +102,3 @@ def test_arithmetic(self):
         self.expect_var_path("my_ref - 1", value="1")
         self.expect_var_path("ref + my_ref", value="4")
         self.expect_var_path("ref - my_ref", value="0")
-
-        # TODO: Pointer arithmetics
-        self.expect(
-            "frame var -- 'p + 1'",
-            error=True,
-            substrs=["invalid operands to binary expression ('int *' and 'int')"],
-        )
-        self.expect(
-            "frame var -- 'p - 1'",
-            error=True,
-            substrs=["invalid operands to binary expression ('int *' and 'int')"],
-        )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
index 448cd5b1ec7e0..b027fab1e176f 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py
@@ -22,8 +22,121 @@ def test_pointer_arithmetic(self):
         self.expect_var_path("+array", type="int *")
         self.expect_var_path("+array_ref", type="int *")
         self.expect_var_path("+p_int0", type="int *")
+
+        # Binary operations
+        self.expect_var_path("p_char1", type="const char *")
+        self.expect_var_path("p_char1 + 1", type="const char *")
+        self.expect_var_path("p_char1 + offset", type="const char *")
+
+        self.expect_var_path("my_p_char1", type="my_char_ptr")
+        self.expect_var_path("my_p_char1 + 1", type="my_char_ptr")
+        self.expect_var_path("my_p_char1 + offset", type="my_char_ptr")
+
+        self.expect_var_path("*(p_char1 + 0)", value="'h'")
+        self.expect_var_path("*(4 + p_char1)", value="'o'")
+        self.expect_var_path("*(p_char1 + offset - 1)", value="'o'")
+
+        self.expect_var_path("*p_int0", value="0")
+        self.expect_var_path("*cp_int5", value="5")
+        self.expect_var_path("*(&*(cp_int5 + 1) - 1)", value="5")
+
+        self.expect_var_path("p_int0 - p_int0", value="0", type="__ptrdiff_t")
+        self.expect_var_path("cp_int5 - p_int0", value="5", type="__ptrdiff_t")
+        self.expect_var_path("cp_int5 - td_int_ptr0", value="5", type="__ptrdiff_t")
+        self.expect_var_path("td_int_ptr0 - cp_int5", value="-5", type="__ptrdiff_t")
+
+        # Check arrays
+        self.expect_var_path("array + 1", type="int *")
+        self.expect_var_path("1 + array", type="int *")
+        self.expect_var_path("array_ref + 1", type="int *")
+        self.expect_var_path("1 + array_ref", type="int *")
+        self.expect_var_path("array - 1", type="int *")
+        self.expect_var_path("array_ref - 1", type="int *")
+        self.expect_var_path("array - array", value="0", type="__ptrdiff_t")
+        self.expect_var_path("array - array_ref", value="0", type="__ptrdiff_t")
+        self.expect_var_path("array_ref - array_ref", value="0", type="__ptrdiff_t")
+
+        # Errors
         self.expect(
             "frame var -- '-p_int0'",
             error=True,
             substrs=["invalid argument type 'int *' to unary expression"],
         )
+        self.expect(
+            "frame var -- 'cp_int5 - p_char1'",
+            error=True,
+            substrs=[
+                "'const int *' and 'const char *' are not pointers to compatible types"
+            ],
+        )
+        self.expect(
+            "frame var -- 'p_int0 + cp_int5'",
+            error=True,
+            substrs=[
+                "invalid operands to binary expression ('int *' and 'const int *')"
+            ],
+        )
+        self.expect(
+            "frame var -- 'p_void + 1'",
+            error=True,
+            substrs=["arithmetic on a pointer to void"],
+        )
+        self.expect(
+            "frame var -- 'p_void - 1'",
+            error=True,
+            substrs=["arithmetic on a pointer to void"],
+        )
+        self.expect(
+            "frame var -- 'p_void - p_char1'",
+            error=True,
+            substrs=[
+                "'void *' and 'const char *' are not pointers to compatible types"
+            ],
+        )
+        self.expect(
+            "frame var -- 'p_void - p_void'",
+            error=True,
+            substrs=["arithmetic on pointers to void"],
+        )
+        self.expect(
+            "frame var -- 'pp_void0 - p_char1'",
+            error=True,
+            substrs=[
+                "'void **' and 'const char *' are not pointers to compatible types"
+            ],
+        )
+        self.expect(
+            "frame var -- 'p_int0 - 1.0'",
+            error=True,
+            substrs=["invalid operands to binary expression ('int *' and 'double')"],
+        )
+        self.expect(
+            "frame var -- '1.0f + p_int0'",
+            error=True,
+            substrs=["invalid operands to binary expression ('float' and 'int *')"],
+        )
+        self.expect(
+            "frame var -- '1 - array'",
+            error=True,
+            substrs=["invalid operands to binary expression ('int' and 'int[10]')"],
+        )
+        self.expect(
+            "frame var -- 'array + array'",
+            error=True,
+            substrs=["invalid operands to binary expression ('int[10]' and 'int[10]')"],
+        )
+        self.expect(
+            "frame var -- 'array + array'",
+            error=True,
+            substrs=["invalid operands to binary expression ('int[10]' and 'int[10]')"],
+        )
+        self.expect(
+            "frame var -- 'int_null + 1'",
+            error=True,
+            substrs=["arithmetic on a nullptr is undefined"],
+        )
+        self.expect(
+            "frame var -- 'int_null - 1'",
+            error=True,
+            substrs=["arithmetic on a nullptr is undefined"],
+        )
diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
index b4e0e88b1ffc9..be1f3424f62dc 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp
@@ -1,11 +1,30 @@
 void stop() {}
 
 int main(int argc, char **argv) {
+  int offset = 5;
   int array[10];
   array[0] = 0;
+  array[offset] = offset;
   int (&array_ref)[10] = array;
   int *p_int0 = &array[0];
 
+  const char *p_char1 = "hello";
+  typedef const char *my_char_ptr;
+  my_char_ptr my_p_char1 = p_char1;
+
+  int **pp_int0 = &p_int0;
+  const int *cp_int0 = &array[0];
+  const int *cp_int5 = &array[offset];
+
+  typedef int *td_int_ptr_t;
+  td_int_ptr_t td_int_ptr0 = &array[0];
+
+  void *p_void = (void *)p_char1;
+  void **pp_void0 = &p_void;
+  void **pp_void1 = pp_void0 + 1;
+
+  int *int_null = nullptr;
+
   stop(); // Set a breakpoint here
   return 0;
 }



More information about the lldb-commits mailing list