[Lldb-commits] [lldb] d637038 - [LLDB] Add unary operators Dereference and AddressOf to DIL (#134428)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Apr 29 09:29:55 PDT 2025
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
+}
More information about the lldb-commits
mailing list