Author: Ilia Kuklin
Date: 2025-04-29T21:29:52+05:00
New Revision: d63703842937c8a089a272297886de5fc7bdc0a4

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

LOG: [LLDB] Add unary operators Dereference and AddressOf to DIL (#134428)

Added: 
    lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile
    
lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
    lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp
    lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile
    
lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
    lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp

Modified: 
    lldb/docs/dil-expr-lang.ebnf
    lldb/include/lldb/ValueObject/DILAST.h
    lldb/include/lldb/ValueObject/DILEval.h
    lldb/include/lldb/ValueObject/DILLexer.h
    lldb/include/lldb/ValueObject/DILParser.h
    lldb/source/Target/StackFrame.cpp
    lldb/source/ValueObject/DILAST.cpp
    lldb/source/ValueObject/DILEval.cpp
    lldb/source/ValueObject/DILLexer.cpp
    lldb/source/ValueObject/DILParser.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 0bbbecbdc78c1..c8bf4231b3e4a 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -3,7 +3,12 @@
 (* This is currently a subset of the final DIL Language, matching the current
    DIL implementation. *)
 
-expression = primary_expression ;
+expression = unary_expression ;
+
+unary_expression = unary_operator expression
+                 | primary_expression ;
+
+unary_operator = "*" | "&" ;
 
 primary_expression = id_expression
                    | "(" expression ")";

diff  --git a/lldb/include/lldb/ValueObject/DILAST.h 
b/lldb/include/lldb/ValueObject/DILAST.h
index 05d87e9cc4b6b..fe3827ef0516a 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -20,6 +20,13 @@ namespace lldb_private::dil {
 enum class NodeKind {
   eErrorNode,
   eIdentifierNode,
+  eUnaryOpNode,
+};
+
+/// The Unary operators recognized by DIL.
+enum class UnaryOpKind {
+  AddrOf, // "&"
+  Deref,  // "*"
 };
 
 /// Forward declaration, for use in DIL AST nodes. Definition is at the very
@@ -81,6 +88,26 @@ class IdentifierNode : public ASTNode {
   std::string m_name;
 };
 
+class UnaryOpNode : public ASTNode {
+public:
+  UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP operand)
+      : ASTNode(location, NodeKind::eUnaryOpNode), m_kind(kind),
+        m_operand(std::move(operand)) {}
+
+  llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+  UnaryOpKind kind() const { return m_kind; }
+  ASTNode *operand() const { return m_operand.get(); }
+
+  static bool classof(const ASTNode *node) {
+    return node->GetKind() == NodeKind::eUnaryOpNode;
+  }
+
+private:
+  UnaryOpKind m_kind;
+  ASTNodeUP m_operand;
+};
+
 /// This class contains one Visit method for each specialized type of
 /// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
 /// the correct function in the DIL expression evaluator for evaluating that
@@ -90,6 +117,8 @@ class Visitor {
   virtual ~Visitor() = default;
   virtual llvm::Expected<lldb::ValueObjectSP>
   Visit(const IdentifierNode *node) = 0;
+  virtual llvm::Expected<lldb::ValueObjectSP>
+  Visit(const UnaryOpNode *node) = 0;
 };
 
 } // namespace lldb_private::dil

diff  --git a/lldb/include/lldb/ValueObject/DILEval.h 
b/lldb/include/lldb/ValueObject/DILEval.h
index 335035d3f9248..b1dd3fdb49739 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -49,6 +49,7 @@ class Interpreter : Visitor {
 private:
   llvm::Expected<lldb::ValueObjectSP>
   Visit(const IdentifierNode *node) override;
+  llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
 
   // Used by the interpreter to create objects, perform casts, etc.
   lldb::TargetSP m_target;

diff  --git a/lldb/include/lldb/ValueObject/DILLexer.h 
b/lldb/include/lldb/ValueObject/DILLexer.h
index d15fc382d1623..3508b8b7a85c6 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -24,11 +24,13 @@ namespace lldb_private::dil {
 class Token {
 public:
   enum Kind {
+    amp,
     coloncolon,
     eof,
     identifier,
     l_paren,
     r_paren,
+    star,
   };
 
   Token(Kind kind, std::string spelling, uint32_t start)

diff  --git a/lldb/include/lldb/ValueObject/DILParser.h 
b/lldb/include/lldb/ValueObject/DILParser.h
index 2689c4e625f09..f5c00b1040ef4 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -43,7 +43,7 @@ class DILDiagnosticError
         m_detail(std::move(detail)) {}
 
   DILDiagnosticError(llvm::StringRef expr, const std::string &message,
-                     uint32_t loc, uint16_t err_len);
+                     uint32_t loc, uint16_t err_len = 1);
 
   std::unique_ptr<CloneableError> Clone() const override {
     return std::make_unique<DILDiagnosticError>(m_detail);
@@ -83,6 +83,7 @@ class DILParser {
   ASTNodeUP Run();
 
   ASTNodeUP ParseExpression();
+  ASTNodeUP ParseUnaryExpression();
   ASTNodeUP ParsePrimaryExpression();
 
   std::string ParseNestedNameSpecifier();

diff  --git a/lldb/source/Target/StackFrame.cpp 
b/lldb/source/Target/StackFrame.cpp
index 46dbc97dbfe26..df9f1eae32bd1 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -538,7 +538,7 @@ ValueObjectSP 
StackFrame::DILGetValueForVariableExpressionPath(
   auto lex_or_err = dil::DILLexer::Create(var_expr);
   if (!lex_or_err) {
     error = Status::FromError(lex_or_err.takeError());
-    return ValueObjectSP();
+    return ValueObjectConstResult::Create(nullptr, std::move(error));
   }
 
   // Parse the expression.
@@ -547,7 +547,7 @@ ValueObjectSP 
StackFrame::DILGetValueForVariableExpressionPath(
       !no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
   if (!tree_or_error) {
     error = Status::FromError(tree_or_error.takeError());
-    return ValueObjectSP();
+    return ValueObjectConstResult::Create(nullptr, std::move(error));
   }
 
   // Evaluate the parsed expression.
@@ -558,7 +558,7 @@ ValueObjectSP 
StackFrame::DILGetValueForVariableExpressionPath(
   auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get());
   if (!valobj_or_error) {
     error = Status::FromError(valobj_or_error.takeError());
-    return ValueObjectSP();
+    return ValueObjectConstResult::Create(nullptr, std::move(error));
   }
 
   return *valobj_or_error;

diff  --git a/lldb/source/ValueObject/DILAST.cpp 
b/lldb/source/ValueObject/DILAST.cpp
index e75958d784627..ea847587501ee 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -19,4 +19,8 @@ llvm::Expected<lldb::ValueObjectSP> 
IdentifierNode::Accept(Visitor *v) const {
   return v->Visit(this);
 }
 
+llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const {
+  return v->Visit(this);
+}
+
 } // namespace lldb_private::dil

diff  --git a/lldb/source/ValueObject/DILEval.cpp 
b/lldb/source/ValueObject/DILEval.cpp
index 4889834c7a3c1..15a66d4866305 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -207,9 +207,11 @@ Interpreter::Interpreter(lldb::TargetSP target, 
llvm::StringRef expr,
       m_exe_ctx_scope(frame_sp) {}
 
 llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) 
{
-
-  // Traverse an AST pointed by the `node`.
-  return node->Accept(this);
+  // Evaluate an AST.
+  auto value_or_error = node->Accept(this);
+  // Return the computed value-or-error. The caller is responsible for
+  // checking if an error occured during the evaluation.
+  return value_or_error;
 }
 
 llvm::Expected<lldb::ValueObjectSP>
@@ -232,4 +234,42 @@ Interpreter::Visit(const IdentifierNode *node) {
   return identifier;
 }
 
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const UnaryOpNode *node) {
+  Status error;
+  auto rhs_or_err = Evaluate(node->operand());
+  if (!rhs_or_err)
+    return rhs_or_err;
+
+  lldb::ValueObjectSP rhs = *rhs_or_err;
+
+  switch (node->kind()) {
+  case UnaryOpKind::Deref: {
+    lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
+    if (dynamic_rhs)
+      rhs = dynamic_rhs;
+
+    lldb::ValueObjectSP child_sp = rhs->Dereference(error);
+    if (error.Fail())
+      return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
+                                                  node->GetLocation());
+
+    return child_sp;
+  }
+  case UnaryOpKind::AddrOf: {
+    Status error;
+    lldb::ValueObjectSP value = rhs->AddressOf(error);
+    if (error.Fail())
+      return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
+                                                  node->GetLocation());
+
+    return value;
+  }
+  }
+
+  // Unsupported/invalid operation.
+  return llvm::make_error<DILDiagnosticError>(
+      m_expr, "invalid ast: unexpected binary operator", node->GetLocation());
+}
+
 } // namespace lldb_private::dil

diff  --git a/lldb/source/ValueObject/DILLexer.cpp 
b/lldb/source/ValueObject/DILLexer.cpp
index 1f013288c839b..b9c2e7971e3b4 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -19,6 +19,8 @@ namespace lldb_private::dil {
 
 llvm::StringRef Token::GetTokenName(Kind kind) {
   switch (kind) {
+  case Kind::amp:
+    return "amp";
   case Kind::coloncolon:
     return "coloncolon";
   case Kind::eof:
@@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
     return "l_paren";
   case Kind::r_paren:
     return "r_paren";
+  case Token::star:
+    return "star";
   }
   llvm_unreachable("Unknown token name");
 }
@@ -82,9 +86,8 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
     return Token(Token::identifier, maybe_word->str(), position);
 
   constexpr std::pair<Token::Kind, const char *> operators[] = {
-      {Token::l_paren, "("},
-      {Token::r_paren, ")"},
-      {Token::coloncolon, "::"},
+      {Token::amp, "&"},     {Token::coloncolon, "::"}, {Token::l_paren, "("},
+      {Token::r_paren, ")"}, {Token::star, "*"},
   };
   for (auto [kind, str] : operators) {
     if (remainder.consume_front(str))

diff  --git a/lldb/source/ValueObject/DILParser.cpp 
b/lldb/source/ValueObject/DILParser.cpp
index 0bd1430e585a7..2c78eae8cf6bf 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -81,7 +81,38 @@ ASTNodeUP DILParser::Run() {
 //  expression:
 //    primary_expression
 //
-ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
+ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
+
+// Parse an unary_expression.
+//
+//  unary_expression:
+//    unary_operator expression
+//    primary_expression
+//
+//  unary_operator:
+//    "&"
+//    "*"
+//
+ASTNodeUP DILParser::ParseUnaryExpression() {
+  if (CurToken().IsOneOf({Token::amp, Token::star})) {
+    Token token = CurToken();
+    uint32_t loc = token.GetLocation();
+    m_dil_lexer.Advance();
+    auto rhs = ParseExpression();
+    switch (token.GetKind()) {
+    case Token::star:
+      return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Deref,
+                                           std::move(rhs));
+    case Token::amp:
+      return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::AddrOf,
+                                           std::move(rhs));
+
+    default:
+      llvm_unreachable("invalid token kind");
+    }
+  }
+  return ParsePrimaryExpression();
+}
 
 // Parse a primary_expression.
 //

diff  --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile 
b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git 
a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
 
b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
new file mode 100644
index 0000000000000..8eab75949047d
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
@@ -0,0 +1,38 @@
+"""
+Test DIL address calculation.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILGlobalVariableLookup(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def expect_var_path(self, expr, compare_to_framevar=False, value=None, 
type=None):
+        value_dil = super().expect_var_path(expr, value=value, type=type)
+        if compare_to_framevar:
+            self.runCmd("settings set target.experimental.use-DIL false")
+            value_frv = super().expect_var_path(expr, value=value, type=type)
+            self.runCmd("settings set target.experimental.use-DIL true")
+            self.assertEqual(value_dil.GetValue(), value_frv.GetValue())
+
+    def test_frame_var(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
+
+        self.runCmd("settings set target.experimental.use-DIL true")
+        self.expect_var_path("&x", True, type="int *")
+        self.expect_var_path("r", True, type="int &")
+        self.expect_var_path("&r", True, type="int &*")
+        self.expect_var_path("pr", True, type="int *&")
+        self.expect_var_path("&pr", True, type="int *&*")
+        self.expect_var_path("my_pr", True)
+        self.expect_var_path("&my_pr", True, type="mypr *")
+        self.expect_var_path("&globalVar", True, type="int *")
+        self.expect_var_path("&s_str", True, type="const char **")
+        self.expect_var_path("&argc", True, type="int *")

diff  --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp 
b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp
new file mode 100644
index 0000000000000..29341e1d8e478
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/main.cpp
@@ -0,0 +1,16 @@
+int globalVar = 0xDEADBEEF;
+
+int main(int argc, char **argv) {
+  int x = 42;
+  int &r = x;
+  int *p = &x;
+  int *&pr = p;
+
+  typedef int *&mypr;
+  mypr my_pr = p;
+
+  const char *s_str = "hello";
+
+  char c = 1;
+  return 0; // Set a breakpoint here
+}

diff  --git 
a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile 
b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git 
a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
 
b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
new file mode 100644
index 0000000000000..d36c5fce6d43d
--- /dev/null
+++ 
b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
@@ -0,0 +1,50 @@
+"""
+Test DIL pointer arithmetic.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILGlobalVariableLookup(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def expect_var_path(self, expr, compare_to_framevar=False, value=None, 
type=None):
+        value_dil = super().expect_var_path(expr, value=value, type=type)
+        if compare_to_framevar:
+            self.runCmd("settings set target.experimental.use-DIL false")
+            value_frv = super().expect_var_path(expr, value=value, type=type)
+            self.runCmd("settings set target.experimental.use-DIL true")
+            self.assertEqual(value_dil.GetValue(), value_frv.GetValue())
+
+    def test_dereference(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+        )
+
+        self.runCmd("settings set target.experimental.use-DIL true")
+        self.expect_var_path("*p_int0", True, value="0")
+        self.expect_var_path("*cp_int5", True, value="5")
+        self.expect_var_path("*rcp_int0", True, type="const int *")
+        self.expect_var_path("*offset_p", True, value="5")
+        self.expect_var_path("*offset_pref", True, type="int *")
+        self.expect_var_path("**pp_int0", value="0")
+        self.expect_var_path("&**pp_int0", type="int *")
+        self.expect(
+            "frame var '*array'",
+            error=True,
+            substrs=["not a pointer or reference type"],
+        )
+        self.expect(
+            "frame var '&*p_null'",
+            error=True,
+            substrs=["doesn't have a valid address"],
+        )
+        self.expect(
+            "frame var '&*p_void'",
+            error=True,
+            substrs=["dereference failed: (void *) p_void"],
+        )

diff  --git 
a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp 
b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp
new file mode 100644
index 0000000000000..b43b030fba049
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/main.cpp
@@ -0,0 +1,31 @@
+int main(int argc, char **argv) {
+  int *p_null = nullptr;
+  const char *p_char1 = "hello";
+
+  typedef const char *my_char_ptr;
+  my_char_ptr my_p_char1 = p_char1;
+
+  int offset = 5;
+  int *offset_p = &offset;
+  int *&offset_pref = offset_p;
+  int array[10];
+  array[0] = 0;
+  array[offset] = offset;
+
+  int(&array_ref)[10] = array;
+
+  int *p_int0 = &array[0];
+  int **pp_int0 = &p_int0;
+  const int *cp_int0 = &array[0];
+  const int *cp_int5 = &array[offset];
+  const int *&rcp_int0 = cp_int0;
+
+  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;
+
+  return 0; // Set a breakpoint here
+}


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to