[Lldb-commits] [lldb] [llvm] [DO NOT MERGE][DebugInfo] Implement debug_names's IDX_parent attribute (PR #75365)

Felipe de Azevedo Piovezan via lldb-commits lldb-commits at lists.llvm.org
Wed Dec 13 09:59:56 PST 2023


https://github.com/felipepiovezan created https://github.com/llvm/llvm-project/pull/75365

The commits are meant to be looked at one at a time

>From ece936d56a22dad7f604d6610d02a9a59bac0345 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Mon, 11 Dec 2023 12:42:40 -0300
Subject: [PATCH 1/9] [DWARFDeclContext] Add helper function to extract
 qualified names as vector

squash_23540ceb4639
---
 lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp | 6 ++++++
 lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h   | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp
index 44421c0eda3eec..3cdb47d50bbfc0 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp
@@ -55,6 +55,12 @@ const char *DWARFDeclContext::GetQualifiedName() const {
   return m_qualified_name.c_str();
 }
 
+llvm::SmallVector<llvm::StringRef>
+DWARFDeclContext::GetQualifiedNameAsVector() const {
+  return llvm::to_vector_of<llvm::StringRef>(
+      llvm::map_range(m_entries, GetName));
+}
+
 bool DWARFDeclContext::operator==(const DWARFDeclContext &rhs) const {
   if (m_entries.size() != rhs.m_entries.size())
     return false;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h
index a20a862d340296..40ebb72c91d8f0 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h
@@ -68,6 +68,11 @@ class DWARFDeclContext {
 
   const char *GetQualifiedName() const;
 
+  /// Returns a vector of string, one string per entry in the fully qualified
+  /// name. For example, for the name `A::B::C`, this methods returns `{"A",
+  /// "B", "C"}`
+  llvm::SmallVector<llvm::StringRef> GetQualifiedNameAsVector() const;
+
   // Same as GetQualifiedName, but the life time of the returned string will
   // be that of the LLDB session.
   ConstString GetQualifiedNameAsConstString() const {

>From 81b7009a751430593a6e20aba5dd677ca5ffebe9 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Wed, 6 Dec 2023 11:26:18 -0800
Subject: [PATCH 2/9] [DebugNames] Implement DW_IDX_parent entries

squash_81501dd8dcd1
---
 llvm/include/llvm/CodeGen/AccelTable.h        | 24 ++++++++--
 llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp    | 46 +++++++++++++++++--
 llvm/lib/DWARFLinker/DWARFLinker.cpp          |  8 ++++
 .../DWARFLinkerParallel/DWARFLinkerImpl.cpp   |  3 +-
 .../lib/DWARFLinkerParallel/DWARFLinkerUnit.h |  3 ++
 5 files changed, 74 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/AccelTable.h b/llvm/include/llvm/CodeGen/AccelTable.h
index 0f35fd3514fae7..b6662232ce5137 100644
--- a/llvm/include/llvm/CodeGen/AccelTable.h
+++ b/llvm/include/llvm/CodeGen/AccelTable.h
@@ -262,9 +262,12 @@ class DWARF5AccelTableData : public AccelTableData {
 
   DWARF5AccelTableData(const DIE &Die, const uint32_t UnitID,
                        const bool IsTU = false);
-  DWARF5AccelTableData(const uint64_t DieOffset, const unsigned DieTag,
-                       const unsigned UnitID, const bool IsTU = false)
-      : OffsetVal(DieOffset), DieTag(DieTag), UnitID(UnitID), IsTU(IsTU) {}
+  DWARF5AccelTableData(const uint64_t DieOffset,
+                       const std::optional<uint64_t> ParentOffset,
+                       const unsigned DieTag, const unsigned UnitID,
+                       const bool IsTU = false)
+      : OffsetVal(DieOffset), ParentOffset(ParentOffset), DieTag(DieTag),
+        UnitID(UnitID), IsTU(IsTU) {}
 
 #ifndef NDEBUG
   void print(raw_ostream &OS) const override;
@@ -281,14 +284,24 @@ class DWARF5AccelTableData : public AccelTableData {
   void normalizeDIEToOffset() {
     assert(std::holds_alternative<const DIE *>(OffsetVal) &&
            "Accessing offset after normalizing.");
-    OffsetVal = std::get<const DIE *>(OffsetVal)->getOffset();
+    const DIE *Entry = std::get<const DIE *>(OffsetVal);
+    ParentOffset = Entry->getParent() ? Entry->getParent()->getOffset()
+                                      : std::optional<uint64_t>();
+    OffsetVal = Entry->getOffset();
   }
   bool isNormalized() const {
     return std::holds_alternative<uint64_t>(OffsetVal);
   }
 
+  std::optional<uint64_t> getParentDieOffset() const {
+    assert(std::holds_alternative<uint64_t>(OffsetVal) &&
+           "Accessing DIE Offset before normalizing.");
+    return ParentOffset;
+  }
+
 protected:
   std::variant<const DIE *, uint64_t> OffsetVal;
+  std::optional<uint64_t> ParentOffset;
   uint32_t DieTag : 16;
   uint32_t UnitID : 15;
   uint32_t IsTU : 1;
@@ -337,7 +350,8 @@ class DWARF5AccelTable : public AccelTable<DWARF5AccelTableData> {
     for (auto &Entry : Table.getEntries()) {
       for (AccelTableData *Value : Entry.second.Values) {
         DWARF5AccelTableData *Data = static_cast<DWARF5AccelTableData *>(Value);
-        addName(Entry.second.Name, Data->getDieOffset(), Data->getDieTag(),
+        addName(Entry.second.Name, Data->getDieOffset(),
+                Data->getParentDieOffset(), Data->getDieTag(),
                 Data->getUnitID(), true);
       }
     }
diff --git a/llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp b/llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp
index d6f487c18b0301..f1ad8db8acd4e2 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp
@@ -30,6 +30,9 @@
 
 using namespace llvm;
 
+static cl::opt<bool> EnableParents("enable-debugname-parents", cl::init(false),
+                                   cl::Hidden);
+
 void AccelTableBase::computeBucketCount() {
   // First get the number of unique hashes.
   std::vector<uint32_t> Uniques;
@@ -224,6 +227,8 @@ class Dwarf5AccelTableWriter : public AccelTableWriter {
   MCSymbol *EntryPool = Asm->createTempSymbol("names_entries");
   // Indicates if this module is built with Split Dwarf enabled.
   bool IsSplitDwarf = false;
+  DenseMap<uint64_t, MCSymbol *> DieOffsetToAccelEntrySymbol;
+  llvm::DenseSet<MCSymbol *> EmittedAccelEntrySymbols;
 
   void populateAbbrevsMap();
 
@@ -232,8 +237,8 @@ class Dwarf5AccelTableWriter : public AccelTableWriter {
   void emitBuckets() const;
   void emitStringOffsets() const;
   void emitAbbrevs() const;
-  void emitEntry(const DataT &Entry) const;
-  void emitData() const;
+  void emitEntry(const DataT &Entry);
+  void emitData();
 
 public:
   Dwarf5AccelTableWriter(
@@ -414,6 +419,8 @@ static uint32_t constructAbbreviationTag(
   if (EntryRet)
     AbbrvTag |= 1 << EntryRet->Endoding.Index;
   AbbrvTag |= 1 << dwarf::DW_IDX_die_offset;
+  if (EnableParents)
+    AbbrvTag |= 1 << dwarf::DW_IDX_parent;
   AbbrvTag |= Tag << LowerBitSize;
   return AbbrvTag;
 }
@@ -431,6 +438,8 @@ void Dwarf5AccelTableWriter<DataT>::populateAbbrevsMap() {
           if (EntryRet)
             UA.push_back(EntryRet->Endoding);
           UA.push_back({dwarf::DW_IDX_die_offset, dwarf::DW_FORM_ref4});
+          if (EnableParents)
+            UA.push_back({dwarf::DW_IDX_parent, dwarf::DW_FORM_ref4});
           Abbreviations.try_emplace(AbbrvTag, UA);
         }
       }
@@ -507,7 +516,7 @@ void Dwarf5AccelTableWriter<DataT>::emitAbbrevs() const {
 }
 
 template <typename DataT>
-void Dwarf5AccelTableWriter<DataT>::emitEntry(const DataT &Entry) const {
+void Dwarf5AccelTableWriter<DataT>::emitEntry(const DataT &Entry) {
   std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet =
       getIndexForEntry(Entry);
   uint32_t AbbrvTag = constructAbbreviationTag(Entry.getDieTag(), EntryRet);
@@ -516,6 +525,18 @@ void Dwarf5AccelTableWriter<DataT>::emitEntry(const DataT &Entry) const {
          "Why wasn't this abbrev generated?");
   assert(getTagFromAbbreviationTag(AbbrevIt->first) == Entry.getDieTag() &&
          "Invalid Tag");
+
+  // Create a label for this Entry, if not yet created by a IDX_parent
+  // reference to the same underlying DIE.
+  MCSymbol *&EntrySymbol = DieOffsetToAccelEntrySymbol[Entry.getDieOffset()];
+  if (EntrySymbol == nullptr)
+    EntrySymbol = Asm->createTempSymbol("symbol");
+
+  // Emit the label for this Entry, if a label hasn't yet been emitted for some
+  // other Entry of the same underlying DIE (a DIE may have multiple Entries).
+  if (EmittedAccelEntrySymbols.insert(EntrySymbol).second)
+    Asm->OutStreamer->emitLabel(EntrySymbol);
+
   Asm->emitULEB128(AbbrevIt->first, "Abbreviation code");
 
   for (const auto &AttrEnc : AbbrevIt->second) {
@@ -531,13 +552,25 @@ void Dwarf5AccelTableWriter<DataT>::emitEntry(const DataT &Entry) const {
       assert(AttrEnc.Form == dwarf::DW_FORM_ref4);
       Asm->emitInt32(Entry.getDieOffset());
       break;
+    case dwarf::DW_IDX_parent: {
+      // If a DIE is being placed on the table, its parent is always non-null
+      // (top-level DIEs are not placed in the table), though the parent may not
+      // be indexed. Bad input is handled like a parent that is not indexed,
+      // i.e., with an offset that is not in the table.
+      uint64_t ParentOffset = Entry.getParentDieOffset().value_or(-1);
+      MCSymbol *&ParentSymbol = DieOffsetToAccelEntrySymbol[ParentOffset];
+      if (ParentSymbol == nullptr)
+        ParentSymbol = Asm->createTempSymbol("symbol");
+      Asm->emitLabelDifference(ParentSymbol, EntryPool, 4);
+      break;
+    }
     default:
       llvm_unreachable("Unexpected index attribute!");
     }
   }
 }
 
-template <typename DataT> void Dwarf5AccelTableWriter<DataT>::emitData() const {
+template <typename DataT> void Dwarf5AccelTableWriter<DataT>::emitData() {
   Asm->OutStreamer->emitLabel(EntryPool);
   for (auto &Bucket : Contents.getBuckets()) {
     for (auto *Hash : Bucket) {
@@ -549,6 +582,11 @@ template <typename DataT> void Dwarf5AccelTableWriter<DataT>::emitData() const {
       Asm->emitInt8(0);
     }
   }
+  // Any labels not yet emitted refer to DIEs that are not present in the
+  // accelerator table. Point them to end of the table.
+  for (MCSymbol *Symbol : make_second_range(DieOffsetToAccelEntrySymbol))
+    if (EmittedAccelEntrySymbols.insert(Symbol).second)
+      Asm->OutStreamer->emitLabel(Symbol);
 }
 
 template <typename DataT>
diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp
index 1a7ea47adc5ca3..c8e1948326d2b8 100644
--- a/llvm/lib/DWARFLinker/DWARFLinker.cpp
+++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp
@@ -2253,14 +2253,22 @@ void DWARFLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) {
       TheDwarfEmitter->emitPubTypesForUnit(Unit);
     } break;
     case AccelTableKind::DebugNames: {
+      auto ParentOffsetOrNull = [](const DIE *Die) -> std::optional<uint64_t> {
+        if (const DIE *Parent = Die->getParent())
+          return Die->getParent()->getOffset();
+        return std::nullopt;
+      };
       for (const auto &Namespace : Unit.getNamespaces())
         DebugNames.addName(Namespace.Name, Namespace.Die->getOffset(),
+                           ParentOffsetOrNull(Namespace.Die),
                            Namespace.Die->getTag(), Unit.getUniqueID());
       for (const auto &Pubname : Unit.getPubnames())
         DebugNames.addName(Pubname.Name, Pubname.Die->getOffset(),
+                           ParentOffsetOrNull(Pubname.Die),
                            Pubname.Die->getTag(), Unit.getUniqueID());
       for (const auto &Pubtype : Unit.getPubtypes())
         DebugNames.addName(Pubtype.Name, Pubtype.Die->getOffset(),
+                           ParentOffsetOrNull(Pubtype.Die),
                            Pubtype.Die->getTag(), Unit.getUniqueID());
     } break;
     }
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp
index c49b9ef0cdf989..2f514b6140f4aa 100644
--- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp
@@ -1376,7 +1376,8 @@ void DWARFLinkerImpl::emitDWARFv5DebugNamesSection(const Triple &TargetTriple) {
       case DwarfUnit::AccelType::Namespace:
       case DwarfUnit::AccelType::Type: {
         DebugNames->addName(*DebugStrStrings.getExistingEntry(Info.String),
-                            Info.OutOffset, Info.Tag, CU->getUniqueID());
+                            Info.OutOffset, Info.ParentOutOffset, Info.Tag,
+                            CU->getUniqueID());
       } break;
 
       default:
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h
index 9640a8ee711eb0..4ca1920208761c 100644
--- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h
@@ -125,6 +125,9 @@ class DwarfUnit : public OutputSections {
     /// Output offset of the DIE this entry describes.
     uint64_t OutOffset;
 
+    /// Output offset of the parent of the DIE this entry describes.
+    std::optional<uint64_t> ParentOutOffset;
+
     /// Hash of the fully qualified name.
     uint32_t QualifiedNameHash = 0;
 

>From ba88a1f639c23f4bc5e0aba49a6d93ae1ad6c99d Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Thu, 7 Dec 2023 11:24:39 -0800
Subject: [PATCH 3/9] [DebugNames] Implement Entry::GetParentEntry query

squash_0caa45fd3e26
---
 .../llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h     | 12 ++++++++++++
 llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp   | 10 ++++++++++
 llvm/test/CodeGen/X86/dwarf-headers.o                |  0
 3 files changed, 22 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/dwarf-headers.o

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
index b89536bc0c7230..0fabe98210f423 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
@@ -460,6 +460,11 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
     /// Returns the Offset of the DIE within the containing CU or TU.
     std::optional<uint64_t> getDIEUnitOffset() const;
 
+    /// Returns the Entry corresponding to the parent of the DIE represented by
+    /// `this` Entry. If the parent DIE is not indexed by this table, or if this
+    /// table does not track parents through IDX_parent, an error is returned.
+    Expected<DWARFDebugNames::Entry> getParentDIEEntry() const;
+
     /// Return the Abbreviation that can be used to interpret the raw values of
     /// this Accelerator Entry.
     const Abbrev &getAbbrev() const { return *Abbr; }
@@ -609,6 +614,13 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
 
     Expected<Entry> getEntry(uint64_t *Offset) const;
 
+    // Returns the Entry at the relative `Offset` from the start of the Entry
+    // pool.
+    Expected<Entry> getEntryAtRelativeOffset(uint64_t Offset) const {
+      auto OffsetFromSection = Offset + this->EntriesBase;
+      return getEntry(&OffsetFromSection);
+    }
+
     /// Look up all entries in this Name Index matching \c Key.
     iterator_range<ValueIterator> equal_range(StringRef Key) const;
 
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
index 0f9c8ef485d456..b0f9953d46ee85 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
@@ -650,6 +650,16 @@ std::optional<uint64_t> DWARFDebugNames::Entry::getLocalTUIndex() const {
   return std::nullopt;
 }
 
+Expected<DWARFDebugNames::Entry>
+DWARFDebugNames::Entry::getParentDIEEntry() const {
+  // The offset of the accelerator table entry for the parent.
+  std::optional<DWARFFormValue> ParentEntryOff = lookup(dwarf::DW_IDX_parent);
+  if (!ParentEntryOff)
+    return createStringError(errc::illegal_byte_sequence,
+                             "Incorrectly terminated entry list.");
+  return NameIdx->getEntryAtRelativeOffset(ParentEntryOff->getRawUValue());
+}
+
 void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const {
   W.startLine() << formatv("Abbrev: {0:x}\n", Abbr->Code);
   W.startLine() << formatv("Tag: {0}\n", Abbr->Tag);
diff --git a/llvm/test/CodeGen/X86/dwarf-headers.o b/llvm/test/CodeGen/X86/dwarf-headers.o
new file mode 100644
index 00000000000000..e69de29bb2d1d6

>From 5fc385893eb35df8264aec9f5149bd2889317015 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Thu, 7 Dec 2023 11:26:52 -0800
Subject: [PATCH 4/9] [lldb][DWARFUnit] Implement PeekDIEName query

This allows us to not parse the entire DIE.
---
 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp | 7 +++++++
 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h   | 5 +++++
 lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp      | 8 ++++++++
 lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h        | 5 +++++
 4 files changed, 25 insertions(+)

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp
index 553b6a4c551d20..775b7a2e73f512 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp
@@ -191,3 +191,10 @@ DWARFDebugInfo::GetDIE(const DIERef &die_ref) {
     return cu->GetNonSkeletonUnit().GetDIE(die_ref.die_offset());
   return DWARFDIE(); // Not found
 }
+
+llvm::StringRef
+DWARFDebugInfo::PeekDIEName(const DIERef &die_ref) {
+  if(DWARFUnit *cu = GetUnit(die_ref))
+    return cu->GetNonSkeletonUnit().PeekDIEName(die_ref.die_offset());
+  return llvm::StringRef();
+}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h
index d5e48f312ea0e9..a8b5abc3beed2d 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h
@@ -43,6 +43,11 @@ class DWARFDebugInfo {
   bool ContainsTypeUnits();
   DWARFDIE GetDIE(const DIERef &die_ref);
 
+  /// Returns the AT_Name of this DIE, if it exists, without parsing the entire
+  /// compile unit. An empty is string is returned upon error or if the
+  /// attribute is not present.
+  llvm::StringRef PeekDIEName(const DIERef &die_ref);
+
   enum {
     eDumpFlag_Verbose = (1 << 0),  // Verbose dumping
     eDumpFlag_ShowForm = (1 << 1), // Show the DW_form type
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
index 6f771c66a725cf..b82f2df0010c61 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -663,6 +663,14 @@ DWARFUnit::GetDIE(dw_offset_t die_offset) {
   return DWARFDIE(); // Not found
 }
 
+llvm::StringRef DWARFUnit::PeekDIEName(dw_offset_t die_offset) {
+  const DWARFDataExtractor &data = GetData();
+  DWARFDebugInfoEntry die;
+  if (!die.Extract(data, this, &die_offset))
+    return llvm::StringRef();
+  return die.GetName(this);
+}
+
 DWARFUnit &DWARFUnit::GetNonSkeletonUnit() {
   ExtractUnitDIEIfNeeded();
   if (m_dwo)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
index 3f528e913d8cfa..bc225a52e1d030 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
@@ -187,6 +187,11 @@ class DWARFUnit : public UserID {
 
   DWARFDIE GetDIE(dw_offset_t die_offset);
 
+  /// Returns the AT_Name of the DIE at `die_offset`, if it exists, without
+  /// parsing the entire compile unit. An empty is string is returned upon
+  /// error or if the attribute is not present.
+  llvm::StringRef PeekDIEName(dw_offset_t die_offset);
+
   DWARFUnit &GetNonSkeletonUnit();
 
   static uint8_t GetAddressByteSize(const DWARFUnit *cu);

>From bf4ce83e43186221b0a99768171bf8756f9d30b3 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Wed, 13 Dec 2023 13:57:07 -0300
Subject: [PATCH 5/9] [DebugNames] Implement supportsIdxParent query

---
 llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h | 8 ++++++++
 llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp        | 5 +++++
 2 files changed, 13 insertions(+)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
index 0fabe98210f423..13ba8269fc3d19 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
@@ -557,6 +557,7 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
     uint64_t StringOffsetsBase;
     uint64_t EntryOffsetsBase;
     uint64_t EntriesBase;
+    bool HasIdxParent = false;
 
     void dumpCUs(ScopedPrinter &W) const;
     void dumpLocalTUs(ScopedPrinter &W) const;
@@ -770,6 +771,13 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
   /// Return the Name Index covering the compile unit at CUOffset, or nullptr if
   /// there is no Name Index covering that unit.
   const NameIndex *getCUNameIndex(uint64_t CUOffset);
+
+  /// Returns true if all the NameIndices in this table provide IDX_parent
+  /// capabilities.
+  bool supportsIdxParent() const {
+    return all_of(NameIndices,
+                  [](const NameIndex &Idx) { return Idx.HasIdxParent; });
+  }
 };
 
 /// If `Name` is the name of a templated function that includes template
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
index b0f9953d46ee85..ef06cb68665882 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
@@ -578,6 +578,10 @@ Error DWARFDebugNames::NameIndex::extract() {
                              "Section too small: cannot read abbreviations.");
 
   EntriesBase = Offset + Hdr.AbbrevTableSize;
+  HasIdxParent = false;
+  auto IsIdxParent = [](auto IdxFormPair) {
+    return IdxFormPair.Index == dwarf::Index::DW_IDX_parent;
+  };
 
   for (;;) {
     auto AbbrevOr = extractAbbrev(&Offset);
@@ -585,6 +589,7 @@ Error DWARFDebugNames::NameIndex::extract() {
       return AbbrevOr.takeError();
     if (isSentinel(*AbbrevOr))
       return Error::success();
+    HasIdxParent |= any_of(AbbrevOr->Attributes, IsIdxParent);
 
     if (!Abbrevs.insert(std::move(*AbbrevOr)).second)
       return createStringError(errc::invalid_argument,

>From 585b765a4a26ac208321f13a7eb35341c91389d1 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Thu, 7 Dec 2023 11:27:47 -0800
Subject: [PATCH 6/9] [DWARFIndex] Implement GetFullyQualifiedType query

Combines:
1. the accelerator table capability of finding parents
2. the DWARFUnit capability of finding the name of a DIE without parsing the
whole CU.
---
 .../Plugins/SymbolFile/DWARF/DWARFIndex.cpp   | 19 +++++++
 .../Plugins/SymbolFile/DWARF/DWARFIndex.h     |  8 +++
 .../SymbolFile/DWARF/DebugNamesDWARFIndex.cpp | 49 +++++++++++++++++++
 .../SymbolFile/DWARF/DebugNamesDWARFIndex.h   |  5 ++
 4 files changed, 81 insertions(+)

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
index b1c323b101cef3..bc3477d9e86e56 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "DWARFDeclContext.h"
 #include "Plugins/Language/ObjC/ObjCLanguage.h"
 #include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
@@ -112,3 +113,21 @@ void DWARFIndex::ReportInvalidDIERef(DIERef ref, llvm::StringRef name) const {
       "bad die {0:x16} for '{1}')\n",
       ref.die_offset(), name.str().c_str());
 }
+
+void DWARFIndex::GetFullyQualifiedType(
+    const DWARFDeclContext &context,
+    llvm::function_ref<bool(DWARFDIE die)> callback) {
+  auto qualified_names = context.GetQualifiedNameAsVector();
+  if (qualified_names.empty())
+    return;
+  auto parent_names = llvm::makeArrayRef(qualified_names).drop_front();
+  GetTypes(context, [&](DWARFDIE die) {
+    auto original_die = die;
+    for (auto parent_name : parent_names) {
+      die = die.GetParent();
+      if (!die || die.GetName() != parent_name)
+        return false;
+    }
+    return callback(original_die);
+  });
+}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
index 9aadeddbb21753..4fd10a634fc57f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
@@ -53,6 +53,14 @@ class DWARFIndex {
                         llvm::function_ref<bool(DWARFDIE die)> callback) = 0;
   virtual void GetTypes(const DWARFDeclContext &context,
                         llvm::function_ref<bool(DWARFDIE die)> callback) = 0;
+
+  /// Finds all DIEs whose fully qualified name matches `context`. A base
+  /// implementation is provided, and it uses the entire CU to check the DIE
+  /// parent hierarchy. Specializations should override this if they are able
+  /// to provide a faster implementation.
+  virtual void
+  GetFullyQualifiedType(const DWARFDeclContext &context,
+                        llvm::function_ref<bool(DWARFDIE die)> callback);
   virtual void
   GetNamespaces(ConstString name,
                 llvm::function_ref<bool(DWARFDIE die)> callback) = 0;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
index 7c253553d57b48..5cc60a3ca8dad7 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
@@ -218,6 +218,55 @@ void DebugNamesDWARFIndex::GetCompleteObjCClass(
   m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback);
 }
 
+void DebugNamesDWARFIndex::GetFullyQualifiedType(
+    const DWARFDeclContext &context,
+    llvm::function_ref<bool(DWARFDIE die)> callback) {
+  if (!m_debug_names_up->supportsIdxParent())
+    return DWARFIndex::GetFullyQualifiedType(context, callback);
+
+  auto qualified_names = context.GetQualifiedNameAsVector();
+  if (qualified_names.empty())
+    return;
+
+  auto CompareEntryATName = [this](llvm::StringRef expected_name,
+                                   const DebugNames::Entry &entry) {
+    auto maybe_dieoffset = entry.getDIEUnitOffset();
+    if (!maybe_dieoffset)
+      return false;
+    auto die_ref = ToDIERef(entry);
+    if (!die_ref)
+      return false;
+    return expected_name == m_debug_info.PeekDIEName(*die_ref);
+  };
+
+  auto parent_names = llvm::makeArrayRef(qualified_names).drop_front();
+  auto CheckParentChain = [&](DebugNames::Entry entry) {
+    for (auto expected_name : parent_names) {
+      auto maybe_parent_entry = entry.getParentDIEEntry();
+      if (!maybe_parent_entry) {
+        consumeError(maybe_parent_entry.takeError());
+        return false;
+      }
+      entry = *maybe_parent_entry;
+      if (!CompareEntryATName(expected_name, entry))
+        return false;
+    }
+    /// All expected names matched. The remaining entry should have no parent
+    /// for a match to occur.
+    auto maybe_parent_entry = entry.getParentDIEEntry();
+    if (maybe_parent_entry)
+      return false;
+    consumeError(maybe_parent_entry.takeError());
+    return true;
+  };
+
+  auto leaf_matches = m_debug_names_up->equal_range(qualified_names.front());
+  for (const DebugNames::Entry &entry : leaf_matches)
+    if (isType(entry.tag()) && CheckParentChain(entry) &&
+        (!ProcessEntry(entry, callback)))
+      return;
+}
+
 void DebugNamesDWARFIndex::GetTypes(
     ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
   for (const DebugNames::Entry &entry :
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
index 7ce630a56137d1..72d02ca0064969 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
@@ -42,6 +42,11 @@ class DebugNamesDWARFIndex : public DWARFIndex {
   void GetCompleteObjCClass(
       ConstString class_name, bool must_be_implementation,
       llvm::function_ref<bool(DWARFDIE die)> callback) override;
+
+  /// Uses DWARF5's IDX_parent fields, when available, to speed up this query.
+  void GetFullyQualifiedType(
+      const DWARFDeclContext &context,
+      llvm::function_ref<bool(DWARFDIE die)> callback) override;
   void GetTypes(ConstString name,
                 llvm::function_ref<bool(DWARFDIE die)> callback) override;
   void GetTypes(const DWARFDeclContext &context,

>From a753876917d39345275eded75e561f741b20cf5d Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Mon, 11 Dec 2023 12:44:39 -0300
Subject: [PATCH 7/9] [SymbolFileDWARF] Use DWARFIndex::GetFullyQualifiedType
 query

---
 lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index d4c573ecd468c4..c3c79d20bc1d86 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -3115,7 +3115,7 @@ SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(const DWARFDIE &die) {
     }
 
     const DWARFDeclContext die_dwarf_decl_ctx = GetDWARFDeclContext(die);
-    m_index->GetTypes(die_dwarf_decl_ctx, [&](DWARFDIE type_die) {
+    m_index->GetFullyQualifiedType(die_dwarf_decl_ctx, [&](DWARFDIE type_die) {
       // Make sure type_die's language matches the type system we are
       // looking for. We don't want to find a "Foo" type from Java if we
       // are looking for a "Foo" type for C, C++, ObjC, or ObjC++.
@@ -3142,9 +3142,8 @@ SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(const DWARFDIE &die) {
         return true;
       }
 
-      DWARFDeclContext type_dwarf_decl_ctx = GetDWARFDeclContext(type_die);
-
       if (log) {
+        DWARFDeclContext type_dwarf_decl_ctx = GetDWARFDeclContext(type_die);
         GetObjectFile()->GetModule()->LogMessage(
             log,
             "SymbolFileDWARF::"
@@ -3154,10 +3153,6 @@ SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(const DWARFDIE &die) {
             type_dwarf_decl_ctx.GetQualifiedName());
       }
 
-      // Make sure the decl contexts match all the way up
-      if (die_dwarf_decl_ctx != type_dwarf_decl_ctx)
-        return true;
-
       Type *resolved_type = ResolveType(type_die, false);
       if (!resolved_type || resolved_type == DIE_IS_BEING_PARSED)
         return true;

>From a8733ef68947279638c8c2de982a91562530a0f8 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Tue, 12 Dec 2023 16:04:31 -0300
Subject: [PATCH 8/9] [AsmPrinter][AccelTable] Make IDX_parent on by default

---
 llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp b/llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp
index f1ad8db8acd4e2..87c7b547e4d7e3 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp
@@ -30,7 +30,7 @@
 
 using namespace llvm;
 
-static cl::opt<bool> EnableParents("enable-debugname-parents", cl::init(false),
+static cl::opt<bool> EnableParents("enable-debugname-parents", cl::init(true),
                                    cl::Hidden);
 
 void AccelTableBase::computeBucketCount() {

>From 4d3fa42597ae39f41dc338cec1489d885f34eec7 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Tue, 17 Oct 2023 09:27:19 -0700
Subject: [PATCH 9/9] [lldb] Add timer to EvaluateExpression method

---
 lldb/source/Commands/CommandObjectExpression.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp
index 3a2dc11e1e71cc..59ea097fe82378 100644
--- a/lldb/source/Commands/CommandObjectExpression.cpp
+++ b/lldb/source/Commands/CommandObjectExpression.cpp
@@ -414,6 +414,8 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr,
                                                  Stream &output_stream,
                                                  Stream &error_stream,
                                                  CommandReturnObject &result) {
+  auto start = std::chrono::steady_clock::now();
+
   // Don't use m_exe_ctx as this might be called asynchronously after the
   // command object DoExecute has finished when doing multi-line expression
   // that use an input reader...
@@ -514,6 +516,9 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr,
     error_stream.Printf("error: unknown error\n");
   }
 
+  auto end = std::chrono::steady_clock::now();
+  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+  error_stream.Printf("Finished expr in: %" PRIu64, duration.count());
   return (success != eExpressionSetupError &&
           success != eExpressionParseError);
 }



More information about the lldb-commits mailing list