[Lldb-commits] [lldb] [LLDB] Add AST node classes, functions, etc. for Data Inspection Lang… (PR #95738)

via lldb-commits lldb-commits at lists.llvm.org
Tue Jun 18 12:40:37 PDT 2024


https://github.com/cmtice updated https://github.com/llvm/llvm-project/pull/95738

>From c703c473147e3e554a98014319294668a0ec790d Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Sun, 16 Jun 2024 16:32:47 -0700
Subject: [PATCH 1/2] [LLDB] Add AST node classes, functions, etc. for Data
 Inspection Language (DIL).

The Data Inspection Language (DIL), described in
https://discourse.llvm.org/t/rfc-data-inspection-language/69893 includes
its own parser and expression evaluator.

This change defines the AST nodes, classes and functions which are used by the
DIL parser and expression evaluator. It also adds the .ebnf file documenting
the (current) expression language handled by the DIL parser and expression
evaluator.
---
 lldb/docs/dil-expr-lang.ebnf    | 165 ++++++++
 lldb/include/lldb/Core/DILAST.h | 690 ++++++++++++++++++++++++++++++++
 lldb/source/Core/CMakeLists.txt |   1 +
 lldb/source/Core/DILAST.cpp     | 568 ++++++++++++++++++++++++++
 4 files changed, 1424 insertions(+)
 create mode 100644 lldb/docs/dil-expr-lang.ebnf
 create mode 100644 lldb/include/lldb/Core/DILAST.h
 create mode 100644 lldb/source/Core/DILAST.cpp

diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
new file mode 100644
index 0000000000000..40c678c25cda5
--- /dev/null
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -0,0 +1,165 @@
+(* LLDB Debug Expressions, a subset of C++ *)
+(* Insired by https://www.nongnu.org/hcb *)
+
+expression = assignment_expression ;
+
+assignment_expression = conditional_expression
+                        logical_or_expression assignment_operator assignment_expression ;
+
+assignment_operator = "="
+                    | "*="
+                    | "/="
+                    | "%="
+                    | "+="
+                    | "-="
+                    | ">>="
+                    | "<<="
+                    | "&="
+                    | "^="
+                    | "|=" ;
+
+conditional_expression = logical_or_expression
+                       | logical_or_expression "?" expression ":" assignment_expression ;
+
+logical_or_expression = logical_and_expression {"||" logical_and_expression} ;
+
+logical_and_expression = inclusive_or_expression {"&&" inclusive_or_expression} ;
+
+inclusive_or_expression = exclusive_or_expression {"|" exclusive_or_expression} ;
+
+exclusive_or_expression = and_expression {"^" and_expression} ;
+
+and_expression = equality_expression {"&" equality_expression} ;
+
+equality_expression = relational_expression {"==" relational_expression}
+                    | relational_expression {"!=" relational_expression} ;
+
+relational_expression = shift_expression {"<" shift_expression}
+                      | shift_expression {">" shift_expression}
+                      | shift_expression {"<=" shift_expression}
+                      | shift_expression {">=" shift_expression} ;
+
+shift_expression = additive_expression {"<<" additive_expression}
+                 | additive_expression {">>" additive_expression} ;
+
+additive_expression = multiplicative_expression {"+" multiplicative_expression}
+                    | multiplicative_expression {"-" multiplicative_expression} ;
+
+multiplicative_expression = cast_expression {"*" cast_expression}
+                          | cast_expression {"/" cast_expression}
+                          | cast_expression {"%" cast_expression} ;
+
+cast_expression = unary_expression
+                | "(" type_id ")" cast_expression ;
+
+unary_expression = postfix_expression
+                 | "++" cast_expression
+                 | "--" cast_expression
+                 | unary_operator cast_expression
+                 | "sizeof" unary_expression
+                 | "sizeof" "(" type_id ")" ;
+
+unary_operator = "*" | "&" | "+" | "-" | "!" | "~" ;
+
+postfix_expression = primary_expression
+                   | postfix_expression "[" expression "]"
+                   | postfix_expression "." id_expression
+                   | postfix_expression "->" id_expression
+                   | postfix_expression "++"
+                   | postfix_expression "--"
+                   | static_cast "<" type_id ">" "(" expression ")" ;
+                   | dynamic_cast "<" type_id ">" "(" expression ")" ;
+                   | reinterpret_cast "<" type_id ">" "(" expression ")" ;
+
+primary_expression = numeric_literal
+                   | boolean_literal
+                   | pointer_literal
+                   | id_expression
+                   | "this"
+                   | "(" expression ")"
+                   | builtin_func ;
+
+type_id = type_specifier_seq [abstract_declarator] ;
+
+type_specifier_seq = type_specifier [type_specifier_seq] ;
+
+type_specifier = simple_type_specifier
+               | cv_qualifier ;
+
+simple_type_specifier = ["::"] [nested_name_specifier] type_name
+                      | "char"
+                      | "char16_t"
+                      | "char32_t"
+                      | "wchar_t"
+                      | "bool"
+                      | "short"
+                      | "int"
+                      | "long"
+                      | "signed"
+                      | "unsigned"
+                      | "float"
+                      | "double"
+                      | "void" ;
+
+nested_name_specifier = type_name "::"
+                      | namespace_name '::'
+                      | nested_name_specifier identifier "::"
+                      | nested_name_specifier simple_template_id "::"
+
+type_name = class_name
+          | enum_name
+          | typedef_name
+          | simple_template_id ;
+
+class_name = identifier ;
+
+enum_name = identifier ;
+
+typedef_name = identifier ;
+
+simple_template_id = template_name "<" [template_argument_list] ">" ;
+
+template_name = identifier ;
+
+template_argument_list = template_argument
+                       | template_argument_list "," template_argument ;
+
+template_argument = type_id
+                  | numeric_literal
+                  | id_expression ;
+
+namespace_name = identifier ;
+
+cv_qualifier = "const" | "volatile" ;
+
+cv_qualifier_seq = cv_qualifier [cv_qualifier_seq] ;
+
+abstract_declarator = ptr_operator [abstract_declarator] ;
+
+ptr_operator = "*" [cv_qualifier_seq]
+             | "&" ;
+
+id_expression = unqualified_id
+              | qualified_id ;
+
+unqualified_id = identifier ;
+
+qualified_id = ["::"] [nested_name_specifier] unqualified_id
+             | ["::"] identifier ;
+
+identifier = ? clang::tok::identifier ? ;
+
+numeric_literal = ? clang::tok::numeric_constant ? ;
+
+boolean_literal = "true" | "false" ;
+
+pointer_literal = "nullptr" ;
+
+builtin_func = builtin_func_name "(" [builtin_func_argument_list] ")" ;
+
+builtin_func_name = "__log2" ;
+
+builtin_func_argument_list = builtin_func_argument
+                           | builtin_func_argument_list "," builtin_func_argument
+
+builtin_func_argument = expression ;
diff --git a/lldb/include/lldb/Core/DILAST.h b/lldb/include/lldb/Core/DILAST.h
new file mode 100644
index 0000000000000..acb9da088eda8
--- /dev/null
+++ b/lldb/include/lldb/Core/DILAST.h
@@ -0,0 +1,690 @@
+//===-- DILAST.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DIL_AST_H_
+#define LLDB_DIL_AST_H_
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <variant>
+#include <vector>
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Utility/ConstString.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+
+namespace lldb_private {
+
+/// Struct to hold information about member fields. Used by the parser for the
+/// Data Inspection Language (DIL).
+struct DILMemberInfo {
+  std::optional<std::string> name;
+  CompilerType type;
+  bool is_bitfield;
+  uint32_t bitfield_size_in_bits;
+  bool is_synthetic;
+  bool is_dynamic;
+  lldb::ValueObjectSP val_obj_sp;
+
+  explicit operator bool() const { return type.IsValid(); }
+};
+
+/// This determines if the type is a shared, unique or weak pointer, either
+/// from stdlibc++ or libc+++.
+bool IsSmartPtrType(CompilerType type);
+
+/// Finds the member field with the given name and type, stores the child index
+/// corresponding to the field in the idx vector and returns a DILMemberInfo
+/// struct with appropriate information about the field.
+DILMemberInfo GetFieldWithNameIndexPath(lldb::ValueObjectSP lhs_val_sp,
+                                        CompilerType type,
+                                        const std::string &name,
+                                        std::vector<uint32_t> *idx,
+                                        CompilerType empty_type,
+                                        bool use_synthetic, bool is_dynamic);
+
+std::tuple<DILMemberInfo, std::vector<uint32_t>>
+GetMemberInfo(lldb::ValueObjectSP lhs_val_sp, CompilerType type,
+              const std::string &name, bool use_synthetic);
+
+/// Get the appropriate ValueObjectSP, consulting the use_dynamic and
+/// use_synthetic options passed, acquiring the process & target locks if
+/// appropriate.
+lldb::ValueObjectSP
+DILGetSPWithLock(lldb::ValueObjectSP valobj_sp,
+                 lldb::DynamicValueType use_dynamic = lldb::eNoDynamicValues,
+                 bool use_synthetic = false);
+
+/// The various types DIL AST nodes (used by the DIL parser).
+enum class DILNodeKind {
+  kDILErrorNode,
+  kLiteralNode,
+  kIdentifierNode,
+  kSizeOfNode,
+  kBuiltinFunctionCallNode,
+  kCStyleCastNode,
+  kCxxStaticCastNode,
+  kCxxReinterpretCastNode,
+  kMemberOfNode,
+  kArraySubscriptNode,
+  kBinaryOpNode,
+  kUnaryOpNode,
+  kTernaryOpNode,
+  kSmartPtrToPtrDecay
+};
+
+/// The C-Style casts allowed by DIL.
+enum class CStyleCastKind {
+  kArithmetic,
+  kEnumeration,
+  kPointer,
+  kNullptr,
+  kReference,
+};
+
+/// The Cxx static casts allowed by DIL.
+enum class CxxStaticCastKind {
+  kNoOp,
+  kArithmetic,
+  kEnumeration,
+  kPointer,
+  kNullptr,
+  kBaseToDerived,
+  kDerivedToBase,
+};
+
+/// The binary operators recognized by DIL.
+enum class BinaryOpKind {
+  Mul,       // "*"
+  Div,       // "/"
+  Rem,       // "%"
+  Add,       // "+"
+  Sub,       // "-"
+  Shl,       // "<<"
+  Shr,       // ">>"
+  LT,        // "<"
+  GT,        // ">"
+  LE,        // "<="
+  GE,        // ">="
+  EQ,        // "=="
+  NE,        // "!="
+  And,       // "&"
+  Xor,       // "^"
+  Or,        // "|"
+  LAnd,      // "&&"
+  LOr,       // "||"
+  Assign,    // "="
+  MulAssign, // "*="
+  DivAssign, // "/="
+  RemAssign, // "%="
+  AddAssign, // "+="
+  SubAssign, // "-="
+  ShlAssign, // "<<="
+  ShrAssign, // ">>="
+  AndAssign, // "&="
+  XorAssign, // "^="
+  OrAssign,  // "|="
+};
+
+/// The Unary operators recognized by DIL.
+enum class UnaryOpKind {
+  PostInc, // "++"
+  PostDec, // "--"
+  PreInc,  // "++"
+  PreDec,  // "--"
+  AddrOf,  // "&"
+  Deref,   // "*"
+  Plus,    // "+"
+  Minus,   // "-"
+  Not,     // "~"
+  LNot,    // "!"
+};
+
+/// Helper functions for DIL AST node parsing.
+
+/// Translates clang tokens to BinaryOpKind.
+BinaryOpKind
+clang_token_kind_to_binary_op_kind(clang::tok::TokenKind token_kind);
+
+/// Returns bool indicating whether or not the input kind is an assignment.
+bool binary_op_kind_is_comp_assign(BinaryOpKind kind);
+
+/// Given a string representing a type, returns the CompilerType corresponding
+/// to the named type, if it exists.
+CompilerType
+ResolveTypeByName(const std::string &name,
+                  std::shared_ptr<ExecutionContextScope> ctx_scope);
+
+/// Quick lookup to check if a type name already exists in a
+/// name-to-CompilerType map the DIL parser keeps of previously found
+/// name/type pairs.
+bool IsContextVar(const std::string &name);
+
+/// Checks to see if the CompilerType is a Smart Pointer (shared, unique, weak)
+/// or not. Only applicable for C++, which is why this is here and not part of
+/// the CompilerType class.
+bool IsSmartPtrType(CompilerType type);
+
+/// Class used to store & manipulate information about identifiers.
+class IdentifierInfo {
+private:
+  using MemberPath = std::vector<uint32_t>;
+  using IdentifierInfoPtr = std::unique_ptr<IdentifierInfo>;
+
+public:
+  enum class Kind {
+    kValue,
+    kContextArg,
+    kMemberPath,
+    kThisKeyword,
+  };
+
+  static IdentifierInfoPtr FromValue(lldb::ValueObjectSP value_sp) {
+    CompilerType type;
+    lldb::ValueObjectSP value = DILGetSPWithLock(value_sp);
+    if (value)
+      type = value->GetCompilerType();
+    return IdentifierInfoPtr(new IdentifierInfo(Kind::kValue, type, value, {}));
+  }
+
+  static IdentifierInfoPtr FromContextArg(CompilerType type) {
+    lldb::ValueObjectSP empty_value;
+    return IdentifierInfoPtr(
+        new IdentifierInfo(Kind::kContextArg, type, empty_value, {}));
+  }
+
+  static IdentifierInfoPtr FromMemberPath(CompilerType type, MemberPath path) {
+    lldb::ValueObjectSP empty_value;
+    return IdentifierInfoPtr(new IdentifierInfo(Kind::kMemberPath, type,
+                                                empty_value, std::move(path)));
+  }
+
+  static IdentifierInfoPtr FromThisKeyword(CompilerType type) {
+    lldb::ValueObjectSP empty_value;
+    return IdentifierInfoPtr(
+        new IdentifierInfo(Kind::kThisKeyword, type, empty_value, {}));
+  }
+
+  Kind kind() const { return m_kind; }
+  lldb::ValueObjectSP value() const { return m_value; }
+  const MemberPath &path() const { return m_path; }
+
+  CompilerType GetType() { return m_type; }
+  bool IsValid() const { return m_type.IsValid(); }
+
+  IdentifierInfo(Kind kind, CompilerType type, lldb::ValueObjectSP value,
+                 MemberPath path)
+      : m_kind(kind), m_type(type), m_value(std::move(value)),
+        m_path(std::move(path)) {}
+
+private:
+  Kind m_kind;
+  CompilerType m_type;
+  lldb::ValueObjectSP m_value;
+  MemberPath m_path;
+};
+
+/// Given the name of an identifier (variable name, member name, type name,
+/// etc.), find the ValueObject for that name (if it exists) and create and
+/// return an IdentifierInfo object containing all the relevant information
+/// about that object (for DIL parsing and evaluating).
+std::unique_ptr<IdentifierInfo> LookupIdentifier(
+    const std::string &name, std::shared_ptr<ExecutionContextScope> ctx_scope,
+    lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr = nullptr);
+
+/// Forward declaration, for use in DIL AST nodes. Definition is at the very
+/// end of this file.
+class DILVisitor;
+
+/// The rest of the classes in this file, except for the DILVisitor class at the
+/// very end, define all the types of AST nodes used by the DIL parser and
+/// expression evaluator. The DIL parser parses the input string and creates the
+/// AST parse tree from the AST nodes. The resulting AST node tree gets passed
+/// to the DIL expression evaluator, which evaluates the DIL AST nodes and
+/// creates/returns a ValueObjectSP containing the result.
+
+/// Base class for AST nodes used by the Data Inspection Language (DIL) parser.
+/// All of the specialized types of AST nodes inherit from this (virtual) base
+/// class.
+class DILASTNode {
+public:
+  DILASTNode(clang::SourceLocation location) : location_(location) {}
+  virtual ~DILASTNode() {}
+
+  virtual void Accept(DILVisitor *v) const = 0;
+
+  virtual bool is_error() const { return false; };
+  virtual bool is_rvalue() const = 0;
+  virtual bool is_bitfield() const { return false; };
+  virtual bool is_context_var() const { return false; };
+  virtual bool is_literal_zero() const { return false; }
+  virtual uint32_t bitfield_size() const { return 0; }
+  virtual CompilerType result_type() const = 0;
+
+  virtual DILNodeKind what_am_i() const = 0;
+
+  clang::SourceLocation location() const { return location_; }
+
+  // The expression result type, but dereferenced in case it's a reference. This
+  // is for convenience, since for the purposes of the semantic analysis only
+  // the dereferenced type matters.
+  CompilerType result_type_deref() const;
+
+private:
+  clang::SourceLocation location_;
+};
+
+using ParseResult = std::unique_ptr<DILASTNode>;
+
+class DILErrorNode : public DILASTNode {
+public:
+  DILErrorNode(CompilerType empty_type)
+      : DILASTNode(clang::SourceLocation()), m_empty_type(empty_type) {}
+  void Accept(DILVisitor *v) const override;
+  bool is_error() const override { return true; }
+  bool is_rvalue() const override { return false; }
+  CompilerType result_type() const override { return m_empty_type; }
+  CompilerType result_type_real() const { return m_empty_type; }
+  DILNodeKind what_am_i() const override { return DILNodeKind::kDILErrorNode; }
+
+private:
+  CompilerType m_empty_type;
+};
+
+class LiteralNode : public DILASTNode {
+public:
+  template <typename ValueT>
+  LiteralNode(clang::SourceLocation location, CompilerType type, ValueT &&value,
+              bool is_literal_zero)
+      : DILASTNode(location), m_type(type),
+        m_value(std::forward<ValueT>(value)),
+        m_is_literal_zero(is_literal_zero) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return true; }
+  bool is_literal_zero() const override { return m_is_literal_zero; }
+  CompilerType result_type() const override { return m_type; }
+  DILNodeKind what_am_i() const override { return DILNodeKind::kLiteralNode; }
+
+  template <typename ValueT> ValueT value() const {
+    return std::get<ValueT>(m_value);
+  }
+
+  auto value() const { return m_value; }
+
+private:
+  CompilerType m_type;
+  std::variant<llvm::APInt, llvm::APFloat, bool, std::vector<char>> m_value;
+  bool m_is_literal_zero;
+};
+
+class IdentifierNode : public DILASTNode {
+public:
+  IdentifierNode(clang::SourceLocation location, std::string name,
+                 std::unique_ptr<IdentifierInfo> identifier, bool is_rvalue,
+                 bool is_context_var)
+      : DILASTNode(location), m_is_rvalue(is_rvalue),
+        m_is_context_var(is_context_var), m_name(std::move(name)),
+        m_identifier(std::move(identifier)) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return m_is_rvalue; }
+  bool is_context_var() const override { return m_is_context_var; };
+  CompilerType result_type() const override { return m_identifier->GetType(); }
+  DILNodeKind what_am_i() const override {
+    return DILNodeKind::kIdentifierNode;
+  }
+
+  std::string name() const { return m_name; }
+  const IdentifierInfo &info() const { return *m_identifier; }
+
+private:
+  bool m_is_rvalue;
+  bool m_is_context_var;
+  std::string m_name;
+  std::unique_ptr<IdentifierInfo> m_identifier;
+};
+
+class SizeOfNode : public DILASTNode {
+public:
+  SizeOfNode(clang::SourceLocation location, CompilerType type,
+             CompilerType operand)
+      : DILASTNode(location), m_type(type), m_operand(operand) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return true; }
+  CompilerType result_type() const override { return m_type; }
+  DILNodeKind what_am_i() const override { return DILNodeKind::kSizeOfNode; }
+
+  CompilerType operand() const { return m_operand; }
+
+private:
+  CompilerType m_type;
+  CompilerType m_operand;
+};
+
+class BuiltinFunctionCallNode : public DILASTNode {
+public:
+  BuiltinFunctionCallNode(clang::SourceLocation location,
+                          CompilerType result_type, std::string name,
+                          std::vector<ParseResult> arguments)
+      : DILASTNode(location), m_result_type(result_type),
+        m_name(std::move(name)), m_arguments(std::move(arguments)) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return true; }
+  CompilerType result_type() const override { return m_result_type; }
+  DILNodeKind what_am_i() const override {
+    return DILNodeKind::kBuiltinFunctionCallNode;
+  }
+
+  std::string name() const { return m_name; }
+  const std::vector<ParseResult> &arguments() const { return m_arguments; };
+
+private:
+  CompilerType m_result_type;
+  std::string m_name;
+  std::vector<ParseResult> m_arguments;
+};
+
+class CStyleCastNode : public DILASTNode {
+public:
+  CStyleCastNode(clang::SourceLocation location, CompilerType type,
+                 ParseResult rhs, CStyleCastKind kind)
+      : DILASTNode(location), m_type(type), m_rhs(std::move(rhs)),
+        m_kind(kind) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override {
+    return m_kind != CStyleCastKind::kReference;
+  }
+  CompilerType result_type() const override { return m_type; }
+  DILNodeKind what_am_i() const override {
+    return DILNodeKind::kCStyleCastNode;
+  }
+
+  CompilerType type() const { return m_type; }
+  DILASTNode *rhs() const { return m_rhs.get(); }
+  CStyleCastKind kind() const { return m_kind; }
+
+private:
+  CompilerType m_type;
+  ParseResult m_rhs;
+  CStyleCastKind m_kind;
+};
+
+class CxxStaticCastNode : public DILASTNode {
+public:
+  CxxStaticCastNode(clang::SourceLocation location, CompilerType type,
+                    ParseResult rhs, CxxStaticCastKind kind, bool is_rvalue)
+      : DILASTNode(location), m_type(type), m_rhs(std::move(rhs)), m_kind(kind),
+        m_is_rvalue(is_rvalue) {
+    assert(kind != CxxStaticCastKind::kBaseToDerived &&
+           kind != CxxStaticCastKind::kDerivedToBase &&
+           "invalid constructor for base-to-derived and derived-to-base casts");
+  }
+
+  CxxStaticCastNode(clang::SourceLocation location, CompilerType type,
+                    ParseResult rhs, std::vector<uint32_t> idx, bool is_rvalue)
+      : DILASTNode(location), m_type(type), m_rhs(std::move(rhs)),
+        m_idx(std::move(idx)), m_kind(CxxStaticCastKind::kDerivedToBase),
+        m_is_rvalue(is_rvalue) {}
+
+  CxxStaticCastNode(clang::SourceLocation location, CompilerType type,
+                    ParseResult rhs, uint64_t offset, bool is_rvalue)
+      : DILASTNode(location), m_type(type), m_rhs(std::move(rhs)),
+        m_offset(offset), m_kind(CxxStaticCastKind::kBaseToDerived),
+        m_is_rvalue(is_rvalue) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return m_is_rvalue; }
+  CompilerType result_type() const override { return m_type; }
+  DILNodeKind what_am_i() const override {
+    return DILNodeKind::kCxxStaticCastNode;
+  }
+
+  CompilerType type() const { return m_type; }
+  DILASTNode *rhs() const { return m_rhs.get(); }
+  const std::vector<uint32_t> &idx() const { return m_idx; }
+  uint64_t offset() const { return m_offset; }
+  CxxStaticCastKind kind() const { return m_kind; }
+
+private:
+  CompilerType m_type;
+  ParseResult m_rhs;
+  std::vector<uint32_t> m_idx;
+  uint64_t m_offset = 0;
+  CxxStaticCastKind m_kind;
+  bool m_is_rvalue;
+};
+
+class CxxReinterpretCastNode : public DILASTNode {
+public:
+  CxxReinterpretCastNode(clang::SourceLocation location, CompilerType type,
+                         ParseResult rhs, bool is_rvalue)
+      : DILASTNode(location), m_type(type), m_rhs(std::move(rhs)),
+        m_is_rvalue(is_rvalue) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return m_is_rvalue; }
+  CompilerType result_type() const override { return m_type; }
+  DILNodeKind what_am_i() const override {
+    return DILNodeKind::kCxxReinterpretCastNode;
+  }
+
+  CompilerType type() const { return m_type; }
+  DILASTNode *rhs() const { return m_rhs.get(); }
+
+private:
+  CompilerType m_type;
+  ParseResult m_rhs;
+  bool m_is_rvalue;
+};
+
+class MemberOfNode : public DILASTNode {
+public:
+  MemberOfNode(clang::SourceLocation location, CompilerType result_type,
+               ParseResult lhs, bool is_bitfield, uint32_t bitfield_size,
+               std::vector<uint32_t> member_index, bool is_arrow,
+               bool is_synthetic, bool is_dynamic, ConstString name,
+               lldb::ValueObjectSP valobj_sp)
+      : DILASTNode(location), m_result_type(result_type), m_lhs(std::move(lhs)),
+        m_is_bitfield(is_bitfield), m_bitfield_size(bitfield_size),
+        m_member_index(std::move(member_index)), m_is_arrow(is_arrow),
+        m_is_synthetic(is_synthetic), m_is_dynamic(is_dynamic),
+        m_field_name(name), m_valobj_sp(valobj_sp) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return false; }
+  bool is_bitfield() const override { return m_is_bitfield; }
+  uint32_t bitfield_size() const override { return m_bitfield_size; }
+  CompilerType result_type() const override { return m_result_type; }
+  DILNodeKind what_am_i() const override { return DILNodeKind::kMemberOfNode; }
+
+  DILASTNode *lhs() const { return m_lhs.get(); }
+  const std::vector<uint32_t> &member_index() const { return m_member_index; }
+  bool is_arrow() const { return m_is_arrow; }
+  bool is_synthetic() const { return m_is_synthetic; }
+  bool is_dynamic() const { return m_is_dynamic; }
+  ConstString field_name() const { return m_field_name; }
+  lldb::ValueObjectSP valobj_sp() const { return m_valobj_sp; }
+
+private:
+  CompilerType m_result_type;
+  ParseResult m_lhs;
+  bool m_is_bitfield;
+  uint32_t m_bitfield_size;
+  std::vector<uint32_t> m_member_index;
+  bool m_is_arrow;
+  bool m_is_synthetic;
+  bool m_is_dynamic;
+  ConstString m_field_name;
+  lldb::ValueObjectSP m_valobj_sp;
+};
+
+class ArraySubscriptNode : public DILASTNode {
+public:
+  ArraySubscriptNode(clang::SourceLocation location, CompilerType result_type,
+                     ParseResult base, ParseResult index)
+      : DILASTNode(location), m_result_type(result_type),
+        m_base(std::move(base)), m_index(std::move(index)) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return false; }
+  CompilerType result_type() const override { return m_result_type; }
+  DILNodeKind what_am_i() const override {
+    return DILNodeKind::kArraySubscriptNode;
+  }
+
+  DILASTNode *base() const { return m_base.get(); }
+  DILASTNode *index() const { return m_index.get(); }
+
+private:
+  CompilerType m_result_type;
+  ParseResult m_base;
+  ParseResult m_index;
+};
+
+class BinaryOpNode : public DILASTNode {
+public:
+  BinaryOpNode(clang::SourceLocation location, CompilerType result_type,
+               BinaryOpKind kind, ParseResult lhs, ParseResult rhs,
+               CompilerType comp_assign_type,
+               ValueObject *val_obj_ptr = nullptr)
+      : DILASTNode(location), m_result_type(result_type), m_kind(kind),
+        m_lhs(std::move(lhs)), m_rhs(std::move(rhs)),
+        m_comp_assign_type(comp_assign_type) {
+    if (val_obj_ptr)
+      m_val_obj_sp = val_obj_ptr->GetSP();
+  }
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override {
+    return !binary_op_kind_is_comp_assign(m_kind);
+  }
+  CompilerType result_type() const override { return m_result_type; }
+  DILNodeKind what_am_i() const override { return DILNodeKind::kBinaryOpNode; }
+
+  BinaryOpKind kind() const { return m_kind; }
+  DILASTNode *lhs() const { return m_lhs.get(); }
+  DILASTNode *rhs() const { return m_rhs.get(); }
+  CompilerType comp_assign_type() const { return m_comp_assign_type; }
+  lldb::ValueObjectSP get_valobj_sp() const { return m_val_obj_sp; }
+
+private:
+  CompilerType m_result_type;
+  BinaryOpKind m_kind;
+  ParseResult m_lhs;
+  ParseResult m_rhs;
+  CompilerType m_comp_assign_type;
+  lldb::ValueObjectSP m_val_obj_sp;
+};
+
+class UnaryOpNode : public DILASTNode {
+public:
+  UnaryOpNode(clang::SourceLocation location, CompilerType result_type,
+              UnaryOpKind kind, ParseResult rhs)
+      : DILASTNode(location), m_result_type(result_type), m_kind(kind),
+        m_rhs(std::move(rhs)) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return m_kind != UnaryOpKind::Deref; }
+  CompilerType result_type() const override { return m_result_type; }
+  DILNodeKind what_am_i() const override { return DILNodeKind::kUnaryOpNode; }
+
+  UnaryOpKind kind() const { return m_kind; }
+  DILASTNode *rhs() const { return m_rhs.get(); }
+
+private:
+  CompilerType m_result_type;
+  UnaryOpKind m_kind;
+  ParseResult m_rhs;
+};
+
+class TernaryOpNode : public DILASTNode {
+public:
+  TernaryOpNode(clang::SourceLocation location, CompilerType result_type,
+                ParseResult cond, ParseResult lhs, ParseResult rhs)
+      : DILASTNode(location), m_result_type(result_type),
+        m_cond(std::move(cond)), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override {
+    return m_lhs->is_rvalue() || m_rhs->is_rvalue();
+  }
+  bool is_bitfield() const override {
+    return m_lhs->is_bitfield() || m_rhs->is_bitfield();
+  }
+  CompilerType result_type() const override { return m_result_type; }
+  DILNodeKind what_am_i() const override { return DILNodeKind::kTernaryOpNode; }
+
+  DILASTNode *cond() const { return m_cond.get(); }
+  DILASTNode *lhs() const { return m_lhs.get(); }
+  DILASTNode *rhs() const { return m_rhs.get(); }
+
+private:
+  CompilerType m_result_type;
+  ParseResult m_cond;
+  ParseResult m_lhs;
+  ParseResult m_rhs;
+};
+
+class SmartPtrToPtrDecay : public DILASTNode {
+public:
+  SmartPtrToPtrDecay(clang::SourceLocation location, CompilerType result_type,
+                     ParseResult ptr)
+      : DILASTNode(location), m_result_type(result_type),
+        m_ptr(std::move(ptr)) {}
+
+  void Accept(DILVisitor *v) const override;
+  bool is_rvalue() const override { return false; }
+  CompilerType result_type() const override { return m_result_type; }
+  DILNodeKind what_am_i() const override {
+    return DILNodeKind::kSmartPtrToPtrDecay;
+  }
+
+  DILASTNode *ptr() const { return m_ptr.get(); }
+
+private:
+  CompilerType m_result_type;
+  ParseResult m_ptr;
+};
+
+/// 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
+/// type of AST node.
+class DILVisitor {
+public:
+  virtual ~DILVisitor() {}
+  virtual void Visit(const DILErrorNode *node) = 0;
+  virtual void Visit(const LiteralNode *node) = 0;
+  virtual void Visit(const IdentifierNode *node) = 0;
+  virtual void Visit(const SizeOfNode *node) = 0;
+  virtual void Visit(const BuiltinFunctionCallNode *node) = 0;
+  virtual void Visit(const CStyleCastNode *node) = 0;
+  virtual void Visit(const CxxStaticCastNode *node) = 0;
+  virtual void Visit(const CxxReinterpretCastNode *node) = 0;
+  virtual void Visit(const MemberOfNode *node) = 0;
+  virtual void Visit(const ArraySubscriptNode *node) = 0;
+  virtual void Visit(const BinaryOpNode *node) = 0;
+  virtual void Visit(const UnaryOpNode *node) = 0;
+  virtual void Visit(const TernaryOpNode *node) = 0;
+  virtual void Visit(const SmartPtrToPtrDecay *node) = 0;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_DIL_AST_H_
diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt
index dbc620b91b1ed..794895134db53 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -29,6 +29,7 @@ add_lldb_library(lldbCore
   DebuggerEvents.cpp
   Declaration.cpp
   Disassembler.cpp
+  DILAST.cpp
   DumpDataExtractor.cpp
   DumpRegisterValue.cpp
   DumpRegisterInfo.cpp
diff --git a/lldb/source/Core/DILAST.cpp b/lldb/source/Core/DILAST.cpp
new file mode 100644
index 0000000000000..909c94064c466
--- /dev/null
+++ b/lldb/source/Core/DILAST.cpp
@@ -0,0 +1,568 @@
+//===-- DILAST.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DILAST.h"
+#include "lldb/API/SBType.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/RegisterContext.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <vector>
+
+namespace lldb_private {
+
+lldb::ValueObjectSP DILGetSPWithLock(lldb::ValueObjectSP in_valobj_sp,
+                                     lldb::DynamicValueType use_dynamic,
+                                     bool use_synthetic) {
+  Process::StopLocker stop_locker;
+  std::unique_lock<std::recursive_mutex> lock;
+  Status error;
+
+  if (!in_valobj_sp) {
+    error.SetErrorString("invalid value object");
+    return in_valobj_sp;
+  }
+
+  lldb::ValueObjectSP value_sp = in_valobj_sp;
+
+  Target *target = value_sp->GetTargetSP().get();
+  // If this ValueObject holds an error, then it is valuable for that.
+  if (value_sp->GetError().Fail())
+    return value_sp;
+
+  if (!target)
+    return lldb::ValueObjectSP();
+
+  lock = std::unique_lock<std::recursive_mutex>(target->GetAPIMutex());
+
+  lldb::ProcessSP process_sp(value_sp->GetProcessSP());
+  if (process_sp && !stop_locker.TryLock(&process_sp->GetRunLock())) {
+    // We don't allow people to play around with ValueObject if the process
+    // is running. If you want to look at values, pause the process, then
+    // look.
+    error.SetErrorString("process must be stopped.");
+    return lldb::ValueObjectSP();
+  }
+
+  if (use_dynamic != lldb::eNoDynamicValues) {
+    lldb::ValueObjectSP dynamic_sp = value_sp->GetDynamicValue(use_dynamic);
+    if (dynamic_sp)
+      value_sp = dynamic_sp;
+  }
+
+  if (use_synthetic) {
+    lldb::ValueObjectSP synthetic_sp = value_sp->GetSyntheticValue();
+    if (synthetic_sp)
+      value_sp = synthetic_sp;
+  }
+
+  if (!value_sp)
+    error.SetErrorString("invalid value object");
+
+  return value_sp;
+}
+
+BinaryOpKind
+clang_token_kind_to_binary_op_kind(clang::tok::TokenKind token_kind) {
+  switch (token_kind) {
+  case clang::tok::star:
+    return BinaryOpKind::Mul;
+  case clang::tok::slash:
+    return BinaryOpKind::Div;
+  case clang::tok::percent:
+    return BinaryOpKind::Rem;
+  case clang::tok::plus:
+    return BinaryOpKind::Add;
+  case clang::tok::minus:
+    return BinaryOpKind::Sub;
+  case clang::tok::lessless:
+    return BinaryOpKind::Shl;
+  case clang::tok::greatergreater:
+    return BinaryOpKind::Shr;
+  case clang::tok::less:
+    return BinaryOpKind::LT;
+  case clang::tok::greater:
+    return BinaryOpKind::GT;
+  case clang::tok::lessequal:
+    return BinaryOpKind::LE;
+  case clang::tok::greaterequal:
+    return BinaryOpKind::GE;
+  case clang::tok::equalequal:
+    return BinaryOpKind::EQ;
+  case clang::tok::exclaimequal:
+    return BinaryOpKind::NE;
+  case clang::tok::amp:
+    return BinaryOpKind::And;
+  case clang::tok::caret:
+    return BinaryOpKind::Xor;
+  case clang::tok::pipe:
+    return BinaryOpKind::Or;
+  case clang::tok::ampamp:
+    return BinaryOpKind::LAnd;
+  case clang::tok::pipepipe:
+    return BinaryOpKind::LOr;
+  case clang::tok::equal:
+    return BinaryOpKind::Assign;
+  case clang::tok::starequal:
+    return BinaryOpKind::MulAssign;
+  case clang::tok::slashequal:
+    return BinaryOpKind::DivAssign;
+  case clang::tok::percentequal:
+    return BinaryOpKind::RemAssign;
+  case clang::tok::plusequal:
+    return BinaryOpKind::AddAssign;
+  case clang::tok::minusequal:
+    return BinaryOpKind::SubAssign;
+  case clang::tok::lesslessequal:
+    return BinaryOpKind::ShlAssign;
+  case clang::tok::greatergreaterequal:
+    return BinaryOpKind::ShrAssign;
+  case clang::tok::ampequal:
+    return BinaryOpKind::AndAssign;
+  case clang::tok::caretequal:
+    return BinaryOpKind::XorAssign;
+  case clang::tok::pipeequal:
+    return BinaryOpKind::OrAssign;
+
+  default:
+    break;
+  }
+  llvm_unreachable("did you add an element to BinaryOpKind?");
+}
+
+bool binary_op_kind_is_comp_assign(BinaryOpKind kind) {
+  switch (kind) {
+  case BinaryOpKind::Assign:
+  case BinaryOpKind::MulAssign:
+  case BinaryOpKind::DivAssign:
+  case BinaryOpKind::RemAssign:
+  case BinaryOpKind::AddAssign:
+  case BinaryOpKind::SubAssign:
+  case BinaryOpKind::ShlAssign:
+  case BinaryOpKind::ShrAssign:
+  case BinaryOpKind::AndAssign:
+  case BinaryOpKind::XorAssign:
+  case BinaryOpKind::OrAssign:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
+CompilerType DILASTNode::result_type_deref() const {
+  auto type = result_type();
+  return type.IsReferenceType() ? type.GetNonReferenceType() : type;
+}
+
+static std::unordered_map<std::string, CompilerType> context_args;
+
+bool IsContextVar(const std::string &name) {
+  return context_args.find(name) != context_args.end();
+}
+
+static lldb::ValueObjectSP
+LookupStaticIdentifier(lldb::TargetSP target_sp,
+                       const llvm::StringRef &name_ref,
+                       ConstString unqualified_name) {
+  // List global variable with the same "basename". There can be many matches
+  // from other scopes (namespaces, classes), so we do additional filtering
+  // later.
+  std::vector<lldb::ValueObjectSP> values;
+  VariableList variable_list;
+  ConstString name(name_ref);
+  target_sp->GetImages().FindGlobalVariables(
+      name, (size_t)std::numeric_limits<uint32_t>::max, variable_list);
+  if (!variable_list.Empty()) {
+    ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
+    if (exe_scope == nullptr)
+      exe_scope = target_sp.get();
+    for (const lldb::VariableSP &var_sp : variable_list) {
+      lldb::ValueObjectSP valobj_sp(
+          ValueObjectVariable::Create(exe_scope, var_sp));
+      if (valobj_sp)
+        values.push_back(valobj_sp);
+    }
+  }
+
+  // Find the corrent variable by matching the name. lldb::SBValue::GetName()
+  // can return strings like "::globarVar", "ns::i" or "int const ns::foo"
+  // depending on the version and the platform.
+  for (uint32_t i = 0; i < values.size(); ++i) {
+    lldb::ValueObjectSP val = values[i];
+    llvm::StringRef val_name_sstr = val->GetName().GetStringRef();
+    llvm::StringRef name_sstr = name.GetStringRef();
+
+    if (val->GetVariable() && val->GetVariable()->NameMatches(unqualified_name))
+      return val;
+
+    if (val_name_sstr == name_sstr ||
+        val_name_sstr == llvm::formatv("::{0}", name_sstr).str() ||
+        val_name_sstr.ends_with(llvm::formatv(" {0}", name_sstr).str()) ||
+        val_name_sstr.ends_with(llvm::formatv("*{0}", name_sstr).str()) ||
+        val_name_sstr.ends_with(llvm::formatv("&{0}", name_sstr).str()))
+      return val;
+  }
+  lldb::ValueObjectSP empty_obj_sp;
+  return empty_obj_sp;
+}
+
+struct EnumMember {
+  CompilerType type;
+  ConstString name;
+  llvm::APSInt value;
+};
+
+static std::vector<EnumMember> GetEnumMembers(CompilerType type) {
+  std::vector<EnumMember> enum_member_list;
+  if (type.IsValid()) {
+    type.ForEachEnumerator(
+        [&enum_member_list](const CompilerType &integer_type, ConstString name,
+                            const llvm::APSInt &value) -> bool {
+          EnumMember enum_member = {integer_type, name, value};
+          enum_member_list.push_back(enum_member);
+          return true; // Keep iterating
+        });
+  }
+  return enum_member_list;
+}
+
+CompilerType
+ResolveTypeByName(const std::string &name,
+                  std::shared_ptr<ExecutionContextScope> ctx_scope) {
+  // Internally types don't have global scope qualifier in their names and
+  // LLDB doesn't support queries with it too.
+  llvm::StringRef name_ref(name);
+  bool global_scope = false;
+
+  if (name_ref.starts_with("::")) {
+    name_ref = name_ref.drop_front(2);
+    global_scope = true;
+  }
+
+  std::vector<CompilerType> result_type_list;
+  lldb::TargetSP target_sp = ctx_scope->CalculateTarget();
+  const char *type_name = name_ref.data();
+  if (type_name && type_name[0] && target_sp) {
+    ModuleList &images = target_sp->GetImages();
+    ConstString const_type_name(type_name);
+    TypeQuery query(type_name);
+    TypeResults results;
+    images.FindTypes(nullptr, query, results);
+    for (const lldb::TypeSP &type_sp : results.GetTypeMap().Types())
+      if (type_sp)
+        result_type_list.push_back(type_sp->GetFullCompilerType());
+
+    if (auto process_sp = target_sp->GetProcessSP()) {
+      for (auto *runtime : process_sp->GetLanguageRuntimes()) {
+        if (auto *vendor = runtime->GetDeclVendor()) {
+          auto types = vendor->FindTypes(const_type_name, UINT32_MAX);
+          for (auto type : types)
+            result_type_list.push_back(type);
+        }
+      }
+    }
+
+    if (result_type_list.size() == 0) {
+      for (auto type_system_sp : target_sp->GetScratchTypeSystems())
+        if (auto compiler_type =
+                type_system_sp->GetBuiltinTypeByName(const_type_name))
+          result_type_list.push_back(compiler_type);
+    }
+  }
+
+  // We've found multiple types, try finding the "correct" one.
+  CompilerType full_match;
+  std::vector<CompilerType> partial_matches;
+
+  for (uint32_t i = 0; i < result_type_list.size(); ++i) {
+    CompilerType type = result_type_list[i];
+    llvm::StringRef type_name_ref = type.GetTypeName().GetStringRef();
+    ;
+
+    if (type_name_ref == name_ref)
+      full_match = type;
+    else if (type_name_ref.ends_with(name_ref))
+      partial_matches.push_back(type);
+  }
+
+  if (global_scope) {
+    // Look only for full matches when looking for a globally qualified type.
+    if (full_match.IsValid())
+      return full_match;
+  } else {
+    // We're looking for type, but there may be multiple candidates and which
+    // one is correct may depend on the currect scope. For now just pick the
+    // most "probable" type.
+
+    // Full match is always correct if we're currently in the global scope.
+    if (full_match.IsValid())
+      return full_match;
+
+    // If we have partial matches, pick a "random" one.
+    if (partial_matches.size() > 0)
+      return partial_matches.back();
+  }
+
+  CompilerType empty_type;
+  return empty_type;
+}
+
+static lldb::VariableSP DILFindVariable(ConstString name,
+                                        VariableList *variable_list) {
+  lldb::VariableSP exact_match;
+  std::vector<lldb::VariableSP> possible_matches;
+
+  typedef std::vector<lldb::VariableSP> collection;
+  typedef collection::iterator iterator;
+
+  iterator pos, end = variable_list->end();
+  for (pos = variable_list->begin(); pos != end; ++pos) {
+    llvm::StringRef str_ref_name = pos->get()->GetName().GetStringRef();
+    // Check for global vars, which might start with '::'.
+    if (str_ref_name.size() > 2 && str_ref_name[0] == ':' &&
+        str_ref_name[1] == ':')
+      str_ref_name = str_ref_name.drop_front(2);
+
+    ConstString tmp_name(str_ref_name);
+    if (tmp_name == name)
+      possible_matches.push_back(*pos);
+    else if (pos->get()->NameMatches(name))
+      possible_matches.push_back(*pos);
+  }
+
+  // Look for exact matches (favors local vars over global vars)
+  for (auto var_sp : possible_matches)
+    if (var_sp->GetName() == name) {
+      exact_match = var_sp;
+      break;
+    }
+
+  if (!exact_match)
+    // Look for a global var exact match.
+    for (auto var_sp : possible_matches) {
+      llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
+      if (str_ref_name.size() > 2 && str_ref_name[0] == ':' &&
+          str_ref_name[1] == ':')
+        str_ref_name = str_ref_name.drop_front(2);
+      ConstString tmp_name(str_ref_name);
+      if (tmp_name == name) {
+        exact_match = var_sp;
+        break;
+      }
+    }
+
+  // Take any match at this point.
+  if (!exact_match && possible_matches.size() > 0)
+    exact_match = possible_matches[0];
+
+  return exact_match;
+}
+
+std::unique_ptr<IdentifierInfo>
+LookupIdentifier(const std::string &name,
+                 std::shared_ptr<ExecutionContextScope> ctx_scope,
+                 lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr) {
+  auto context_arg = context_args.find(name);
+  if (context_arg != context_args.end())
+    return IdentifierInfo::FromContextArg(context_arg->second);
+
+  ConstString name_str(name);
+  llvm::StringRef name_ref = name_str.GetStringRef();
+
+  // Support $rax as a special syntax for accessing registers.
+  // Will return an invalid value in case the requested register doesn't exist.
+  if (name_ref.starts_with("$")) {
+    lldb::ValueObjectSP value_sp;
+    const char *reg_name = name_ref.drop_front(1).data();
+    Target *target = ctx_scope->CalculateTarget().get();
+    Process *process = ctx_scope->CalculateProcess().get();
+    if (target && process) {
+      Process::StopLocker stop_locker;
+      if (stop_locker.TryLock(&process->GetRunLock())) {
+        StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
+        if (stack_frame) {
+          lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
+          if (reg_ctx) {
+            if (const RegisterInfo *reg_info =
+                    reg_ctx->GetRegisterInfoByName(reg_name))
+              value_sp =
+                  ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+          }
+        }
+      }
+    }
+    return IdentifierInfo::FromValue(value_sp);
+  }
+
+  // Internally values don't have global scope qualifier in their names and
+  // LLDB doesn't support queries with it too.
+  bool global_scope = false;
+  if (name_ref.starts_with("::")) {
+    name_ref = name_ref.drop_front(2);
+    global_scope = true;
+  }
+
+  // If the identifier doesn't refer to the global scope and doesn't have any
+  // other scope qualifiers, try looking among the local and instance variables.
+  if (!global_scope && !name_ref.contains("::")) {
+    if (!scope_ptr || !scope_ptr->IsValid()) {
+      // Lookup in the current frame.
+      lldb::StackFrameSP frame = ctx_scope->CalculateStackFrame();
+      // Try looking for a local variable in current scope.
+      lldb::ValueObjectSP value_sp;
+      lldb::VariableListSP var_list_sp(frame->GetInScopeVariableList(true));
+      VariableList *variable_list = var_list_sp.get();
+      if (variable_list) {
+        lldb::VariableSP var_sp =
+            DILFindVariable(ConstString(name_ref), variable_list);
+        if (var_sp)
+          value_sp = frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
+      }
+      if (!value_sp)
+        value_sp = frame->FindVariable(ConstString(name_ref));
+
+      bool use_synthetic = false;
+      lldb::ValueObjectSP value(
+          DILGetSPWithLock(value_sp, use_dynamic, use_synthetic));
+      if (value)
+        // Force static value, otherwise we can end up with the "real" type.
+        return IdentifierInfo::FromValue(value);
+
+      // Try looking for an instance variable (class member).
+      ConstString this_string("this");
+      value = frame->FindVariable(this_string);
+      if (value)
+        value = value->GetChildMemberWithName(name_ref.data());
+
+      if (value)
+        // Force static value, otherwise we can end up with the "real" type.
+        return IdentifierInfo::FromValue(value->GetStaticValue());
+
+    } else {
+      // In a "value" scope `this` refers to the scope object itself.
+      if (name_ref == "this")
+        return IdentifierInfo::FromThisKeyword(scope_ptr->GetPointerType());
+
+      // Lookup the variable as a member of the current scope value.
+      lldb::ValueObjectSP empty_sp;
+      bool use_synthetic = false;
+      auto [member, path] =
+          GetMemberInfo(empty_sp, *scope_ptr, name_ref.data(), use_synthetic);
+      if (member)
+        return IdentifierInfo::FromMemberPath(member.type, std::move(path));
+    }
+  }
+
+  // Try looking for a global or static variable.
+
+  lldb::ValueObjectSP value;
+  if (!global_scope) {
+    // Try looking for static member of the current scope value, e.g.
+    // `ScopeType::NAME`. NAME can include nested struct (`Nested::SUBNAME`),
+    // but it cannot be part of the global scope (start with "::").
+    const char *type_name = "";
+    if (scope_ptr)
+      type_name = scope_ptr->GetCanonicalType().GetTypeName().AsCString();
+    std::string name_with_type_prefix =
+        llvm::formatv("{0}::{1}", type_name, name_ref).str();
+    value = LookupStaticIdentifier(ctx_scope->CalculateTarget(),
+                                   name_with_type_prefix, name_str);
+  }
+
+  // Lookup a regular global variable.
+  if (!value)
+    value = LookupStaticIdentifier(ctx_scope->CalculateTarget(), name_ref,
+                                   name_str);
+
+  // Try looking up enum value.
+  if (!value && name_ref.contains("::")) {
+    auto [enum_typename, enumerator_name] = name_ref.rsplit("::");
+
+    auto type = ResolveTypeByName(enum_typename.str(), ctx_scope);
+    std::vector<EnumMember> enum_members = GetEnumMembers(type);
+
+    for (size_t i = 0; i < enum_members.size(); i++) {
+      EnumMember enum_member = enum_members[i];
+      if (enum_member.name == enumerator_name) {
+        uint64_t bytes = enum_member.value.getZExtValue();
+        uint64_t byte_size = 0;
+        if (auto temp = type.GetByteSize(ctx_scope.get()))
+          byte_size = temp.value();
+        lldb::TargetSP target_sp = ctx_scope->CalculateTarget();
+        lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>(
+            &bytes, byte_size, target_sp->GetArchitecture().GetByteOrder(),
+            static_cast<uint8_t>(
+                target_sp->GetArchitecture().GetAddressByteSize()));
+        ExecutionContext exe_ctx(
+            ExecutionContextRef(ExecutionContext(target_sp.get(), false)));
+        value = ValueObject::CreateValueObjectFromData("result", *data_sp,
+                                                       exe_ctx, type);
+        break;
+      }
+    }
+  }
+
+  // Last resort, lookup as a register (e.g. `rax` or `rip`).
+  if (!value) {
+    Target *target = ctx_scope->CalculateTarget().get();
+    Process *process = ctx_scope->CalculateProcess().get();
+    if (target && process) {
+      Process::StopLocker stop_locker;
+      if (stop_locker.TryLock(&process->GetRunLock())) {
+        StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
+        if (stack_frame) {
+          lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
+          if (reg_ctx) {
+            if (const RegisterInfo *reg_info =
+                    reg_ctx->GetRegisterInfoByName(name_ref.data()))
+              value =
+                  ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+          }
+        }
+      }
+    }
+  }
+
+  // Force static value, otherwise we can end up with the "real" type.
+  return IdentifierInfo::FromValue(value);
+}
+
+void DILErrorNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void LiteralNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void IdentifierNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void SizeOfNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void BuiltinFunctionCallNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void CStyleCastNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void CxxStaticCastNode::Accept(DILVisitor *v) const { return v->Visit(this); }
+
+void CxxReinterpretCastNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void MemberOfNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void ArraySubscriptNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void BinaryOpNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void UnaryOpNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void TernaryOpNode::Accept(DILVisitor *v) const { v->Visit(this); }
+
+void SmartPtrToPtrDecay::Accept(DILVisitor *v) const { v->Visit(this); }
+
+} // namespace lldb_private

>From d38cdf05f5bcbb0ad34abe67caa09bf137ca0259 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Tue, 18 Jun 2024 12:39:26 -0700
Subject: [PATCH 2/2] [LLDB] Add AST node classes, functions, etc. for Data
 Inspection Language

Fix semicolons in the .ebnf file.
---
 lldb/docs/dil-expr-lang.ebnf | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 40c678c25cda5..37eb870162abf 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -67,8 +67,8 @@ postfix_expression = primary_expression
                    | postfix_expression "->" id_expression
                    | postfix_expression "++"
                    | postfix_expression "--"
-                   | static_cast "<" type_id ">" "(" expression ")" ;
-                   | dynamic_cast "<" type_id ">" "(" expression ")" ;
+                   | static_cast "<" type_id ">" "(" expression ")"
+                   | dynamic_cast "<" type_id ">" "(" expression ")"
                    | reinterpret_cast "<" type_id ">" "(" expression ")" ;
 
 primary_expression = numeric_literal
@@ -104,7 +104,7 @@ simple_type_specifier = ["::"] [nested_name_specifier] type_name
 nested_name_specifier = type_name "::"
                       | namespace_name '::'
                       | nested_name_specifier identifier "::"
-                      | nested_name_specifier simple_template_id "::"
+                      | nested_name_specifier simple_template_id "::";
 
 type_name = class_name
           | enum_name
@@ -160,6 +160,6 @@ builtin_func = builtin_func_name "(" [builtin_func_argument_list] ")" ;
 builtin_func_name = "__log2" ;
 
 builtin_func_argument_list = builtin_func_argument
-                           | builtin_func_argument_list "," builtin_func_argument
+                           | builtin_func_argument_list "," builtin_func_argument;
 
 builtin_func_argument = expression ;



More information about the lldb-commits mailing list