Skip to content

Commit 58c4f08

Browse files
committed
Add unary operators Dereference and AddressOf
1 parent 5fbd065 commit 58c4f08

File tree

9 files changed

+245
-9
lines changed

9 files changed

+245
-9
lines changed

lldb/docs/dil-expr-lang.ebnf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
(* This is currently a subset of the final DIL Language, matching the current
44
DIL implementation. *)
55
6-
expression = primary_expression ;
6+
expression = unary_expression ;
7+
8+
unary_expression = unary_operator primary_expression ;
79
810
primary_expression = id_expression
911
| "(" expression ")";

lldb/include/lldb/ValueObject/DILAST.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ namespace lldb_private::dil {
2020
enum class NodeKind {
2121
eErrorNode,
2222
eIdentifierNode,
23+
eUnaryOpNode,
24+
};
25+
26+
/// The Unary operators recognized by DIL.
27+
enum class UnaryOpKind {
28+
AddrOf, // "&"
29+
Deref, // "*"
2330
};
2431

2532
/// Forward declaration, for use in DIL AST nodes. Definition is at the very
@@ -44,6 +51,8 @@ class ASTNode {
4451

4552
virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;
4653

54+
virtual bool is_rvalue() const { return false; }
55+
4756
uint32_t GetLocation() const { return m_location; }
4857
NodeKind GetKind() const { return m_kind; }
4958

@@ -81,6 +90,27 @@ class IdentifierNode : public ASTNode {
8190
std::string m_name;
8291
};
8392

93+
class UnaryOpNode : public ASTNode {
94+
public:
95+
UnaryOpNode(uint32_t location, UnaryOpKind kind, ASTNodeUP rhs)
96+
: ASTNode(location, NodeKind::eUnaryOpNode), m_kind(kind),
97+
m_rhs(std::move(rhs)) {}
98+
99+
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
100+
bool is_rvalue() const override { return m_kind != UnaryOpKind::Deref; }
101+
102+
UnaryOpKind kind() const { return m_kind; }
103+
ASTNode *rhs() const { return m_rhs.get(); }
104+
105+
static bool classof(const ASTNode *node) {
106+
return node->GetKind() == NodeKind::eUnaryOpNode;
107+
}
108+
109+
private:
110+
UnaryOpKind m_kind;
111+
ASTNodeUP m_rhs;
112+
};
113+
84114
/// This class contains one Visit method for each specialized type of
85115
/// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
86116
/// the correct function in the DIL expression evaluator for evaluating that
@@ -90,6 +120,8 @@ class Visitor {
90120
virtual ~Visitor() = default;
91121
virtual llvm::Expected<lldb::ValueObjectSP>
92122
Visit(const IdentifierNode *node) = 0;
123+
virtual llvm::Expected<lldb::ValueObjectSP>
124+
Visit(const UnaryOpNode *node) = 0;
93125
};
94126

95127
} // namespace lldb_private::dil

lldb/include/lldb/ValueObject/DILEval.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref,
3838
lldb::DynamicValueType use_dynamic,
3939
CompilerType *scope_ptr = nullptr);
4040

41+
class FlowAnalysis {
42+
public:
43+
FlowAnalysis(bool address_of_is_pending)
44+
: m_address_of_is_pending(address_of_is_pending) {}
45+
46+
bool AddressOfIsPending() const { return m_address_of_is_pending; }
47+
void DiscardAddressOf() { m_address_of_is_pending = false; }
48+
49+
private:
50+
bool m_address_of_is_pending;
51+
};
52+
4153
class Interpreter : Visitor {
4254
public:
4355
Interpreter(lldb::TargetSP target, llvm::StringRef expr,
@@ -47,12 +59,29 @@ class Interpreter : Visitor {
4759
llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node);
4860

4961
private:
62+
llvm::Expected<lldb::ValueObjectSP>
63+
EvaluateNode(const ASTNode *node, FlowAnalysis *flow = nullptr);
64+
5065
llvm::Expected<lldb::ValueObjectSP>
5166
Visit(const IdentifierNode *node) override;
67+
llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override;
68+
69+
lldb::ValueObjectSP EvaluateDereference(lldb::ValueObjectSP rhs);
70+
71+
FlowAnalysis *flow_analysis() { return m_flow_analysis_chain.back(); }
5272

5373
// Used by the interpreter to create objects, perform casts, etc.
5474
lldb::TargetSP m_target;
5575
llvm::StringRef m_expr;
76+
// Flow analysis chain represents the expression evaluation flow for the
77+
// current code branch. Each node in the chain corresponds to an AST node,
78+
// describing the semantics of the evaluation for it. Currently, flow analysis
79+
// propagates the information about the pending address-of operator, so that
80+
// combination of address-of and a subsequent dereference can be eliminated.
81+
// End of the chain (i.e. `back()`) contains the flow analysis instance for
82+
// the current node. It may be `nullptr` if no relevant information is
83+
// available, the caller/user is supposed to check.
84+
std::vector<FlowAnalysis *> m_flow_analysis_chain;
5685
lldb::ValueObjectSP m_scope;
5786
lldb::DynamicValueType m_default_dynamic;
5887
std::shared_ptr<StackFrame> m_exe_ctx_scope;

lldb/include/lldb/ValueObject/DILLexer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ namespace lldb_private::dil {
2424
class Token {
2525
public:
2626
enum Kind {
27+
amp,
2728
coloncolon,
2829
eof,
2930
identifier,
3031
l_paren,
3132
r_paren,
33+
star,
3234
};
3335

3436
Token(Kind kind, std::string spelling, uint32_t start)

lldb/include/lldb/ValueObject/DILParser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class DILDiagnosticError
4343
m_detail(std::move(detail)) {}
4444

4545
DILDiagnosticError(llvm::StringRef expr, const std::string &message,
46-
uint32_t loc, uint16_t err_len);
46+
uint32_t loc, uint16_t err_len = 1);
4747

4848
std::unique_ptr<CloneableError> Clone() const override {
4949
return std::make_unique<DILDiagnosticError>(m_detail);
@@ -83,6 +83,7 @@ class DILParser {
8383
ASTNodeUP Run();
8484

8585
ASTNodeUP ParseExpression();
86+
ASTNodeUP ParseUnaryExpression();
8687
ASTNodeUP ParsePrimaryExpression();
8788

8889
std::string ParseNestedNameSpecifier();

lldb/source/ValueObject/DILAST.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
1919
return v->Visit(this);
2020
}
2121

22+
llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const {
23+
return v->Visit(this);
24+
}
25+
2226
} // namespace lldb_private::dil

lldb/source/ValueObject/DILEval.cpp

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@
1818

1919
namespace lldb_private::dil {
2020

21+
static lldb::ValueObjectSP
22+
ArrayToPointerConversion(lldb::ValueObjectSP valobj,
23+
std::shared_ptr<ExecutionContextScope> ctx) {
24+
assert(valobj->IsArrayType() &&
25+
"an argument to array-to-pointer conversion must be an array");
26+
27+
uint64_t addr = valobj->GetLoadAddress();
28+
llvm::StringRef name = "result";
29+
ExecutionContext exe_ctx;
30+
ctx->CalculateExecutionContext(exe_ctx);
31+
return ValueObject::CreateValueObjectFromAddress(
32+
name, addr, exe_ctx,
33+
valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(),
34+
/* do_deref */ false);
35+
}
36+
2137
static lldb::ValueObjectSP LookupStaticIdentifier(
2238
VariableList &variable_list, std::shared_ptr<StackFrame> exe_scope,
2339
llvm::StringRef name_ref, llvm::StringRef unqualified_name) {
@@ -206,10 +222,25 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
206222
: m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
207223
m_exe_ctx_scope(frame_sp) {}
208224

209-
llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) {
225+
llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *tree) {
226+
// Evaluate an AST.
227+
auto value_or_error = EvaluateNode(tree);
228+
229+
// Return the computed result-or-error.
230+
return value_or_error;
231+
}
210232

233+
llvm::Expected<lldb::ValueObjectSP>
234+
Interpreter::EvaluateNode(const ASTNode *node, FlowAnalysis *flow) {
235+
// Set up the evaluation context for the current node.
236+
m_flow_analysis_chain.push_back(flow);
211237
// Traverse an AST pointed by the `node`.
212-
return node->Accept(this);
238+
auto value_or_error = node->Accept(this);
239+
// Cleanup the context.
240+
m_flow_analysis_chain.pop_back();
241+
// Return the computed value-or-error. The caller is responsible for
242+
// checking if an error occured during the evaluation.
243+
return value_or_error;
213244
}
214245

215246
llvm::Expected<lldb::ValueObjectSP>
@@ -232,4 +263,106 @@ Interpreter::Visit(const IdentifierNode *node) {
232263
return identifier;
233264
}
234265

235-
} // namespace lldb_private::dil
266+
llvm::Expected<lldb::ValueObjectSP>
267+
Interpreter::Visit(const UnaryOpNode *node) {
268+
FlowAnalysis rhs_flow(
269+
/* address_of_is_pending */ node->kind() == UnaryOpKind::AddrOf);
270+
271+
Status error;
272+
auto rhs_or_err = EvaluateNode(node->rhs(), &rhs_flow);
273+
if (!rhs_or_err) {
274+
return rhs_or_err;
275+
}
276+
lldb::ValueObjectSP rhs = *rhs_or_err;
277+
278+
if (rhs->GetCompilerType().IsReferenceType()) {
279+
rhs = rhs->Dereference(error);
280+
if (error.Fail()) {
281+
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
282+
node->GetLocation());
283+
}
284+
}
285+
CompilerType rhs_type = rhs->GetCompilerType();
286+
switch (node->kind()) {
287+
case UnaryOpKind::Deref: {
288+
if (rhs_type.IsArrayType())
289+
rhs = ArrayToPointerConversion(rhs, m_exe_ctx_scope);
290+
291+
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
292+
if (dynamic_rhs)
293+
rhs = dynamic_rhs;
294+
295+
if (rhs->GetCompilerType().IsPointerType())
296+
return EvaluateDereference(rhs);
297+
lldb::ValueObjectSP child_sp = rhs->Dereference(error);
298+
if (error.Success())
299+
rhs = child_sp;
300+
301+
return rhs;
302+
}
303+
case UnaryOpKind::AddrOf: {
304+
if (node->rhs()->is_rvalue()) {
305+
std::string errMsg =
306+
llvm::formatv("cannot take the address of an rvalue of type {0}",
307+
rhs_type.TypeDescription());
308+
return llvm::make_error<DILDiagnosticError>(m_expr, errMsg,
309+
node->GetLocation());
310+
}
311+
if (rhs->IsBitfield()) {
312+
return llvm::make_error<DILDiagnosticError>(
313+
m_expr, "address of bit-field requested", node->GetLocation());
314+
}
315+
// If the address-of operation wasn't cancelled during the evaluation of
316+
// RHS (e.g. because of the address-of-a-dereference elision), apply it
317+
// here.
318+
if (rhs_flow.AddressOfIsPending()) {
319+
Status error;
320+
lldb::ValueObjectSP value = rhs->AddressOf(error);
321+
if (error.Fail()) {
322+
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
323+
node->GetLocation());
324+
}
325+
return value;
326+
}
327+
return rhs;
328+
}
329+
}
330+
331+
// Unsupported/invalid operation.
332+
return llvm::make_error<DILDiagnosticError>(
333+
m_expr, "invalid ast: unexpected binary operator", node->GetLocation(),
334+
1);
335+
}
336+
337+
lldb::ValueObjectSP Interpreter::EvaluateDereference(lldb::ValueObjectSP rhs) {
338+
// If rhs is a reference, dereference it first.
339+
Status error;
340+
if (rhs->GetCompilerType().IsReferenceType())
341+
rhs = rhs->Dereference(error);
342+
343+
assert(rhs->GetCompilerType().IsPointerType() &&
344+
"invalid ast: must be a pointer type");
345+
346+
if (rhs->GetDerefValobj())
347+
return rhs->GetDerefValobj()->GetSP();
348+
349+
CompilerType pointer_type = rhs->GetCompilerType();
350+
lldb::addr_t base_addr = rhs->GetValueAsUnsigned(0);
351+
352+
llvm::StringRef name = "result";
353+
ExecutionContext exe_ctx(m_target.get(), false);
354+
lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress(
355+
name, base_addr, exe_ctx, pointer_type,
356+
/* do_deref */ false);
357+
358+
// If we're in the address-of context, skip the dereference and cancel the
359+
// pending address-of operation as well.
360+
if (flow_analysis() && flow_analysis()->AddressOfIsPending()) {
361+
flow_analysis()->DiscardAddressOf();
362+
return value;
363+
}
364+
365+
return value->Dereference(error);
366+
}
367+
368+
} // namespace lldb_private::dil

lldb/source/ValueObject/DILLexer.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ namespace lldb_private::dil {
1919

2020
llvm::StringRef Token::GetTokenName(Kind kind) {
2121
switch (kind) {
22+
case Kind::amp:
23+
return "amp";
2224
case Kind::coloncolon:
2325
return "coloncolon";
2426
case Kind::eof:
@@ -29,6 +31,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
2931
return "l_paren";
3032
case Kind::r_paren:
3133
return "r_paren";
34+
case Token::star:
35+
return "star";
3236
}
3337
llvm_unreachable("Unknown token name");
3438
}
@@ -82,9 +86,8 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
8286
return Token(Token::identifier, maybe_word->str(), position);
8387

8488
constexpr std::pair<Token::Kind, const char *> operators[] = {
85-
{Token::l_paren, "("},
86-
{Token::r_paren, ")"},
87-
{Token::coloncolon, "::"},
89+
{Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("},
90+
{Token::r_paren, ")"}, {Token::star, "*"},
8891
};
8992
for (auto [kind, str] : operators) {
9093
if (remainder.consume_front(str))

lldb/source/ValueObject/DILParser.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,37 @@ ASTNodeUP DILParser::Run() {
8282
// expression:
8383
// primary_expression
8484
//
85-
ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
85+
ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); }
86+
87+
// Parse an unary_expression.
88+
//
89+
// unary_expression:
90+
// unary_operator primary_expression
91+
//
92+
// unary_operator:
93+
// "&"
94+
// "*"
95+
//
96+
ASTNodeUP DILParser::ParseUnaryExpression() {
97+
if (CurToken().IsOneOf({Token::amp, Token::star})) {
98+
Token token = CurToken();
99+
uint32_t loc = token.GetLocation();
100+
m_dil_lexer.Advance();
101+
auto rhs = ParsePrimaryExpression();
102+
switch (token.GetKind()) {
103+
case Token::star:
104+
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Deref,
105+
std::move(rhs));
106+
case Token::amp:
107+
return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::AddrOf,
108+
std::move(rhs));
109+
110+
default:
111+
llvm_unreachable("invalid token kind");
112+
}
113+
}
114+
return ParsePrimaryExpression();
115+
}
86116

87117
// Parse a primary_expression.
88118
//

0 commit comments

Comments
 (0)