[Lldb-commits] [lldb] [LLDB] Add unary operators Dereference and AddressOf to DIL (PR #134428)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Apr 4 11:04:41 PDT 2025
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
More information about the lldb-commits
mailing list