llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Ilia Kuklin (kuilpd) <details> <summary>Changes</summary> --- Full diff: https://github.com/llvm/llvm-project/pull/134428.diff 9 Files Affected: - (modified) lldb/docs/dil-expr-lang.ebnf (+3-1) - (modified) lldb/include/lldb/ValueObject/DILAST.h (+32) - (modified) lldb/include/lldb/ValueObject/DILEval.h (+29) - (modified) lldb/include/lldb/ValueObject/DILLexer.h (+2) - (modified) lldb/include/lldb/ValueObject/DILParser.h (+1) - (modified) lldb/source/ValueObject/DILAST.cpp (+4) - (modified) lldb/source/ValueObject/DILEval.cpp (+136-3) - (modified) lldb/source/ValueObject/DILLexer.cpp (+6-3) - (modified) lldb/source/ValueObject/DILParser.cpp (+31-1) ``````````diff diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 0bbbecbdc78c1..13b9cc22e6000 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -3,7 +3,9 @@ (* 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 primary_expression ; primary_expression = id_expression | "(" expression ")"; diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 05d87e9cc4b6b..323ebe8dd49ec 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 @@ -44,6 +51,8 @@ class ASTNode { virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0; + virtual bool is_rvalue() const { return false; } + uint32_t GetLocation() const { return m_location; } NodeKind GetKind() const { return m_kind; } @@ -81,6 +90,27 @@ class IdentifierNode : public ASTNode { std::string m_name; }; +class UnaryOpNode : public ASTNode { +public: + UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP rhs) + : ASTNode(location, NodeKind::eUnaryOpNode), m_kind(kind), + m_rhs(std::move(rhs)) {} + + llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; + bool is_rvalue() const override { return m_kind != UnaryOpKind::Deref; } + + UnaryOpKind kind() const { return m_kind; } + ASTNode *rhs() const { return m_rhs.get(); } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eUnaryOpNode; + } + +private: + UnaryOpKind m_kind; + ASTNodeUP m_rhs; +}; + /// 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 +120,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..0080f4dba9291 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -38,6 +38,18 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref, lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr = nullptr); +class FlowAnalysis { +public: + FlowAnalysis(bool address_of_is_pending) + : m_address_of_is_pending(address_of_is_pending) {} + + bool AddressOfIsPending() const { return m_address_of_is_pending; } + void DiscardAddressOf() { m_address_of_is_pending = false; } + +private: + bool m_address_of_is_pending; +}; + class Interpreter : Visitor { public: Interpreter(lldb::TargetSP target, llvm::StringRef expr, @@ -47,12 +59,29 @@ class Interpreter : Visitor { llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node); private: + llvm::Expected<lldb::ValueObjectSP> + EvaluateNode(const ASTNode *node, FlowAnalysis *flow = nullptr); + llvm::Expected<lldb::ValueObjectSP> Visit(const IdentifierNode *node) override; + llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override; + + lldb::ValueObjectSP EvaluateDereference(lldb::ValueObjectSP rhs); + + FlowAnalysis *flow_analysis() { return m_flow_analysis_chain.back(); } // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; llvm::StringRef m_expr; + // Flow analysis chain represents the expression evaluation flow for the + // current code branch. Each node in the chain corresponds to an AST node, + // describing the semantics of the evaluation for it. Currently, flow analysis + // propagates the information about the pending address-of operator, so that + // combination of address-of and a subsequent dereference can be eliminated. + // End of the chain (i.e. `back()`) contains the flow analysis instance for + // the current node. It may be `nullptr` if no relevant information is + // available, the caller/user is supposed to check. + std::vector<FlowAnalysis *> m_flow_analysis_chain; lldb::ValueObjectSP m_scope; lldb::DynamicValueType m_default_dynamic; std::shared_ptr<StackFrame> m_exe_ctx_scope; 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 9b7a6cd487939..9f09994a54729 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -83,6 +83,7 @@ class DILParser { ASTNodeUP Run(); ASTNodeUP ParseExpression(); + ASTNodeUP ParseUnaryExpression(); ASTNodeUP ParsePrimaryExpression(); std::string ParseNestedNameSpecifier(); 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..b6aa349a62e62 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,6 +18,22 @@ namespace lldb_private::dil { +static lldb::ValueObjectSP +ArrayToPointerConversion(lldb::ValueObjectSP valobj, + std::shared_ptr<ExecutionContextScope> ctx) { + assert(valobj->IsArrayType() && + "an argument to array-to-pointer conversion must be an array"); + + uint64_t addr = valobj->GetLoadAddress(); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx; + ctx->CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + name, addr, exe_ctx, + valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), + /* do_deref */ false); +} + static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr<StackFrame> exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -206,10 +222,25 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr, : m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic), m_exe_ctx_scope(frame_sp) {} -llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) { +llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *tree) { + // Evaluate an AST. + auto value_or_error = EvaluateNode(tree); + + // Return the computed result-or-error. + return value_or_error; +} +llvm::Expected<lldb::ValueObjectSP> +Interpreter::EvaluateNode(const ASTNode *node, FlowAnalysis *flow) { + // Set up the evaluation context for the current node. + m_flow_analysis_chain.push_back(flow); // Traverse an AST pointed by the `node`. - return node->Accept(this); + auto value_or_error = node->Accept(this); + // Cleanup the context. + m_flow_analysis_chain.pop_back(); + // 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 +263,106 @@ Interpreter::Visit(const IdentifierNode *node) { return identifier; } -} // namespace lldb_private::dil +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const UnaryOpNode *node) { + FlowAnalysis rhs_flow( + /* address_of_is_pending */ node->kind() == UnaryOpKind::AddrOf); + + Status error; + auto rhs_or_err = EvaluateNode(node->rhs(), &rhs_flow); + if (!rhs_or_err) { + return rhs_or_err; + } + lldb::ValueObjectSP rhs = *rhs_or_err; + + if (rhs->GetCompilerType().IsReferenceType()) { + rhs = rhs->Dereference(error); + if (error.Fail()) { + return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(), + node->GetLocation(), 1); + } + } + CompilerType rhs_type = rhs->GetCompilerType(); + switch (node->kind()) { + case UnaryOpKind::Deref: { + if (rhs_type.IsArrayType()) + rhs = ArrayToPointerConversion(rhs, m_exe_ctx_scope); + + lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic); + if (dynamic_rhs) + rhs = dynamic_rhs; + + if (rhs->GetCompilerType().IsPointerType()) + return EvaluateDereference(rhs); + lldb::ValueObjectSP child_sp = rhs->Dereference(error); + if (error.Success()) + rhs = child_sp; + + return rhs; + } + case UnaryOpKind::AddrOf: { + if (node->rhs()->is_rvalue()) { + std::string errMsg = + llvm::formatv("cannot take the address of an rvalue of type {0}", + rhs_type.TypeDescription()); + return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, + node->GetLocation(), 1); + } + if (rhs->IsBitfield()) { + return llvm::make_error<DILDiagnosticError>( + m_expr, "address of bit-field requested", node->GetLocation(), 1); + } + // If the address-of operation wasn't cancelled during the evaluation of + // RHS (e.g. because of the address-of-a-dereference elision), apply it + // here. + if (rhs_flow.AddressOfIsPending()) { + Status error; + lldb::ValueObjectSP value = rhs->AddressOf(error); + if (error.Fail()) { + return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(), + node->GetLocation(), 1); + } + return value; + } + return rhs; + } + } + + // Unsupported/invalid operation. + return llvm::make_error<DILDiagnosticError>( + m_expr, "invalid ast: unexpected binary operator", node->GetLocation(), + 1); +} + +lldb::ValueObjectSP Interpreter::EvaluateDereference(lldb::ValueObjectSP rhs) { + // If rhs is a reference, dereference it first. + Status error; + if (rhs->GetCompilerType().IsReferenceType()) + rhs = rhs->Dereference(error); + + assert(rhs->GetCompilerType().IsPointerType() && + "invalid ast: must be a pointer type"); + + if (rhs->GetDerefValobj()) + return rhs->GetDerefValobj()->GetSP(); + + CompilerType pointer_type = rhs->GetCompilerType(); + lldb::addr_t base_addr = rhs->GetValueAsUnsigned(0); + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress( + name, base_addr, exe_ctx, pointer_type, + /* do_deref */ false); + + // If we're in the address-of context, skip the dereference and cancel the + // pending address-of operation as well. + if (flow_analysis() && flow_analysis()->AddressOfIsPending()) { + flow_analysis()->DiscardAddressOf(); + return value; + } + + return value->Dereference(error); +} + +} // namespace lldb_private::dil \ No newline at end of file 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 a8baba2c06e7a..c233c535c0355 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -82,7 +82,37 @@ ASTNodeUP DILParser::Run() { // expression: // primary_expression // -ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); } +ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); } + +// Parse an unary_expression. +// +// unary_expression: +// unary_operator 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 = ParsePrimaryExpression(); + 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. // `````````` </details> https://github.com/llvm/llvm-project/pull/134428 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits