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