[llvm] [DWARFYAML] Implement debug_names support (PR #79666)

Felipe de Azevedo Piovezan via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 5 10:17:34 PST 2024


https://github.com/felipepiovezan updated https://github.com/llvm/llvm-project/pull/79666

>From 96c60d804e809f08960324ee27218fd5a5639ac5 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Fri, 26 Jan 2024 15:02:38 -0800
Subject: [PATCH 1/2] [DWARFYAML] Implement debug_names support

This commit brings support for debug_names in DWARFYAML. It parses YAML and
generates emits a DWARF5 Accelerator table with the following limitations:

1. All forms must have a fixed length (zero length is also ok).
2. Hard-coded support for DWARF 5 and DWARF32.
3. The generated table does not contain a hash index

All of these limitations can be lifted in the future, but for now this is good
enough to enable testing.
---
 llvm/include/llvm/ObjectYAML/DWARFEmitter.h   |   1 +
 llvm/include/llvm/ObjectYAML/DWARFYAML.h      |  49 +++++
 llvm/lib/ObjectYAML/DWARFEmitter.cpp          | 186 ++++++++++++++++++
 llvm/lib/ObjectYAML/DWARFYAML.cpp             |  29 +++
 .../DWARF/DWARFAcceleratorTableTest.cpp       | 161 +++++++++++++++
 5 files changed, 426 insertions(+)

diff --git a/llvm/include/llvm/ObjectYAML/DWARFEmitter.h b/llvm/include/llvm/ObjectYAML/DWARFEmitter.h
index ee421b2efc72bc..5e1b88f4fef649 100644
--- a/llvm/include/llvm/ObjectYAML/DWARFEmitter.h
+++ b/llvm/include/llvm/ObjectYAML/DWARFEmitter.h
@@ -42,6 +42,7 @@ Error emitDebugAddr(raw_ostream &OS, const Data &DI);
 Error emitDebugStrOffsets(raw_ostream &OS, const Data &DI);
 Error emitDebugRnglists(raw_ostream &OS, const Data &DI);
 Error emitDebugLoclists(raw_ostream &OS, const Data &DI);
+Error emitDebugNames(raw_ostream &OS, const Data &DI);
 
 std::function<Error(raw_ostream &, const Data &)>
 getDWARFEmitterByName(StringRef SecName);
diff --git a/llvm/include/llvm/ObjectYAML/DWARFYAML.h b/llvm/include/llvm/ObjectYAML/DWARFYAML.h
index a70ddf3a180a2d..5d1213d9ff965b 100644
--- a/llvm/include/llvm/ObjectYAML/DWARFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/DWARFYAML.h
@@ -118,6 +118,28 @@ struct Unit {
   std::vector<Entry> Entries;
 };
 
+struct IdxForm {
+  llvm::dwarf::Index Idx;
+  llvm::dwarf::Form Form;
+};
+
+struct DebugNameAbbreviation {
+  llvm::yaml::Hex64 Code;
+  llvm::dwarf::Tag Tag;
+  std::vector<IdxForm> Indices;
+};
+
+struct DebugNameEntry {
+  llvm::yaml::Hex32 NameStrp;
+  llvm::yaml::Hex64 Code;
+  std::vector<llvm::yaml::Hex64> Values;
+};
+
+struct DebugNamesSection {
+  std::vector<DebugNameAbbreviation> Abbrevs;
+  std::vector<DebugNameEntry> Entries;
+};
+
 struct File {
   StringRef Name;
   uint64_t DirIdx;
@@ -228,6 +250,7 @@ struct Data {
   std::vector<LineTable> DebugLines;
   std::optional<std::vector<ListTable<RnglistEntry>>> DebugRnglists;
   std::optional<std::vector<ListTable<LoclistEntry>>> DebugLoclists;
+  std::optional<DebugNamesSection> DebugNames;
 
   bool isEmpty() const;
 
@@ -276,10 +299,26 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(
     llvm::DWARFYAML::ListEntries<DWARFYAML::LoclistEntry>)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::LoclistEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::DWARFOperation)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::DebugNameEntry)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::DebugNameAbbreviation)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::IdxForm)
 
 namespace llvm {
 namespace yaml {
 
+template <> struct MappingTraits<DWARFYAML::DebugNamesSection> {
+  static void mapping(IO &IO, DWARFYAML::DebugNamesSection &);
+};
+template <> struct MappingTraits<DWARFYAML::DebugNameEntry> {
+  static void mapping(IO &IO, DWARFYAML::DebugNameEntry &);
+};
+template <> struct MappingTraits<DWARFYAML::DebugNameAbbreviation> {
+  static void mapping(IO &IO, DWARFYAML::DebugNameAbbreviation &);
+};
+template <> struct MappingTraits<DWARFYAML::IdxForm> {
+  static void mapping(IO &IO, DWARFYAML::IdxForm &);
+};
+
 template <> struct MappingTraits<DWARFYAML::Data> {
   static void mapping(IO &IO, DWARFYAML::Data &DWARF);
 };
@@ -437,6 +476,16 @@ template <> struct ScalarEnumerationTraits<dwarf::Form> {
   }
 };
 
+#define HANDLE_DW_IDX(unused, name)                                            \
+  io.enumCase(value, "DW_IDX_" #name, dwarf::DW_IDX_##name);
+
+template <> struct ScalarEnumerationTraits<dwarf::Index> {
+  static void enumeration(IO &io, dwarf::Index &value) {
+#include "llvm/BinaryFormat/Dwarf.def"
+    io.enumFallback<Hex16>(value);
+  }
+};
+
 #define HANDLE_DW_UT(unused, name)                                             \
   io.enumCase(value, "DW_UT_" #name, dwarf::DW_UT_##name);
 
diff --git a/llvm/lib/ObjectYAML/DWARFEmitter.cpp b/llvm/lib/ObjectYAML/DWARFEmitter.cpp
index a26e93f65ed7c5..bf01ade9156dbe 100644
--- a/llvm/lib/ObjectYAML/DWARFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/DWARFEmitter.cpp
@@ -691,6 +691,191 @@ Error DWARFYAML::emitDebugStrOffsets(raw_ostream &OS, const Data &DI) {
   return Error::success();
 }
 
+namespace {
+/// Emits the header for a DebugNames section.
+void emitDebugNamesHeader(raw_ostream &OS, bool IsLittleEndian,
+                          uint32_t NameCount, uint32_t AbbrevSize,
+                          uint32_t CombinedSizeOtherParts) {
+  StringRef AugmentationString = "LLVM0700";
+  auto TotalSize = CombinedSizeOtherParts + 5 * sizeof(uint32_t) +
+                   2 * sizeof(uint16_t) + sizeof(NameCount) +
+                   sizeof(AbbrevSize) + AugmentationString.size();
+  writeInteger(uint32_t(TotalSize), OS, IsLittleEndian); // Unit length
+
+  // Everything below is included in total size.
+  writeInteger(uint16_t(5), OS, IsLittleEndian); // Version
+  writeInteger(uint16_t(0), OS, IsLittleEndian); // Padding
+  writeInteger(uint32_t(1), OS, IsLittleEndian); // Compilation Unit count
+  writeInteger(uint32_t(0), OS, IsLittleEndian); // Local Type Unit count
+  writeInteger(uint32_t(0), OS, IsLittleEndian); // Foreign Type Unit count
+  writeInteger(uint32_t(0), OS, IsLittleEndian); // Bucket count
+  writeInteger(NameCount, OS, IsLittleEndian);
+  writeInteger(AbbrevSize, OS, IsLittleEndian);
+  writeInteger(uint32_t(AugmentationString.size()), OS, IsLittleEndian);
+  OS.write(AugmentationString.data(), AugmentationString.size());
+  return;
+}
+
+/// Emits the abbreviations for a DebugNames section.
+std::string
+emitDebugNamesAbbrev(ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
+  std::string Data;
+  llvm::raw_string_ostream OS(Data);
+  for (const auto &Abbrev : Abbrevs) {
+    encodeULEB128(Abbrev.Code, OS);
+    encodeULEB128(Abbrev.Tag, OS);
+    for (auto [Idx, Form] : Abbrev.Indices) {
+      encodeULEB128(Idx, OS);
+      encodeULEB128(Form, OS);
+    }
+    encodeULEB128(0, OS);
+    encodeULEB128(0, OS);
+  }
+  encodeULEB128(0, OS);
+  return Data;
+}
+
+/// Emits a simple CU offsets list for a DebugNames section containing a single
+/// CU at offset 0.
+std::string emitDebugNamesCUOffsets(bool IsLittleEndian) {
+  std::string Data;
+  llvm::raw_string_ostream OS(Data);
+  writeInteger(uint32_t(0), OS, IsLittleEndian);
+  return Data;
+}
+
+/// Emits the "NameTable" for a DebugNames section; according to the spec, it
+/// consists of two arrays: an array of string offsets, followed immediately by
+/// an array of entry offsets. The string offsets are emitted in the order
+/// provided in `Entries`.
+std::string emitDebugNamesNameTable(
+    bool IsLittleEndian,
+    const std::map<uint32_t, std::vector<DWARFYAML::DebugNameEntry>> &Entries,
+    ArrayRef<uint32_t> EntryPoolOffsets) {
+  assert(Entries.size() == EntryPoolOffsets.size());
+
+  std::string Data;
+  llvm::raw_string_ostream OS(Data);
+
+  for (auto Strp : make_first_range(Entries))
+    writeInteger(Strp, OS, IsLittleEndian);
+  for (auto PoolOffset : EntryPoolOffsets)
+    writeInteger(uint32_t(PoolOffset), OS, IsLittleEndian);
+  return Data;
+}
+
+/// Groups entries based on their name (strp) code and returns a sorted map.
+std::map<uint32_t, std::vector<DWARFYAML::DebugNameEntry>>
+groupEntries(ArrayRef<DWARFYAML::DebugNameEntry> Entries) {
+  std::map<uint32_t, std::vector<DWARFYAML::DebugNameEntry>> Ans;
+  for (const auto &Entry : Entries)
+    Ans[Entry.NameStrp].push_back(Entry);
+  return Ans;
+}
+
+/// Finds the abbreviation whose code is AbbrevCode and returns a list
+/// containing the expected size of all non-zero-length forms.
+Expected<SmallVector<uint8_t>>
+getNonZeroDataSizesFor(uint32_t AbbrevCode,
+                       ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
+  const auto *AbbrevIt = find_if(Abbrevs, [&](const auto &Abbrev) {
+    return Abbrev.Code.value == AbbrevCode;
+  });
+  if (AbbrevIt == Abbrevs.end())
+    return createStringError(inconvertibleErrorCode(),
+                             "Did not find an Abbreviation for this code");
+
+  SmallVector<uint8_t> DataSizes;
+  dwarf::FormParams Params{5 /*Version*/, 4 /*AddrSize*/, dwarf::DWARF32};
+  for (auto [Idx, Form] : AbbrevIt->Indices) {
+    auto FormSize = dwarf::getFixedFormByteSize(Form, Params);
+    if (FormSize == std::nullopt)
+      return createStringError(inconvertibleErrorCode(),
+                               "Unsupported Form for YAML debug_names emitter");
+    if (FormSize == 0)
+      continue;
+    DataSizes.push_back(*FormSize);
+  }
+  return DataSizes;
+}
+
+struct PoolOffsetsAndData {
+  std::string PoolData;
+  std::vector<uint32_t> PoolOffsets;
+};
+
+/// Emits the entry pool and returns an array of offsets containing the start
+/// offset for the entries of each unique name.
+/// Verifies that the provided number of data values match those expected by
+/// the abbreviation table.
+Expected<PoolOffsetsAndData> emitDebugNamesEntryPool(
+    bool IsLittleEndian,
+    const std::map<uint32_t, std::vector<DWARFYAML::DebugNameEntry>>
+        &SortedEntries,
+    ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
+  PoolOffsetsAndData Ans;
+  llvm::raw_string_ostream OS(Ans.PoolData);
+
+  for (ArrayRef<DWARFYAML::DebugNameEntry> EntriesWithSameName :
+       llvm::make_second_range(SortedEntries)) {
+    Ans.PoolOffsets.push_back(Ans.PoolData.size());
+
+    for (const auto &Entry : EntriesWithSameName) {
+      encodeULEB128(Entry.Code, OS);
+
+      auto DataSizes = getNonZeroDataSizesFor(Entry.Code, Abbrevs);
+      if (!DataSizes)
+        return DataSizes.takeError();
+      if (DataSizes->size() != Entry.Values.size())
+        return createStringError(
+            inconvertibleErrorCode(),
+            "Mismatch between provided and required number of values");
+
+      for (auto [Value, ValueSize] : zip_equal(Entry.Values, *DataSizes))
+        if (auto E =
+                writeVariableSizedInteger(Value, ValueSize, OS, IsLittleEndian))
+          return std::move(E);
+    }
+    encodeULEB128(0, OS);
+  }
+
+  return Ans;
+}
+} // namespace
+
+Error DWARFYAML::emitDebugNames(raw_ostream &OS, const Data &DI) {
+  assert(DI.DebugNames && "unexpected emitDebugNames() call");
+  const auto DebugNames = DI.DebugNames.value();
+
+  auto SortedEntries = groupEntries(DebugNames.Entries);
+
+  // Emit all sub-sections into individual strings so that we may compute
+  // relative offsets and sizes.
+  Expected<PoolOffsetsAndData> PoolInfo = emitDebugNamesEntryPool(
+      DI.IsLittleEndian, SortedEntries, DebugNames.Abbrevs);
+  if (!PoolInfo)
+    return PoolInfo.takeError();
+  auto NamesTableData = emitDebugNamesNameTable(
+      DI.IsLittleEndian, SortedEntries, PoolInfo->PoolOffsets);
+
+  auto AbbrevData = emitDebugNamesAbbrev(DebugNames.Abbrevs);
+  auto CUOffsetsData = emitDebugNamesCUOffsets(DI.IsLittleEndian);
+
+  auto TotalSize = PoolInfo->PoolData.size() + NamesTableData.size() +
+                   AbbrevData.size() + CUOffsetsData.size();
+
+  // Start real emission by combining all individual strings.
+  emitDebugNamesHeader(OS, DI.IsLittleEndian, SortedEntries.size(),
+                       AbbrevData.size(), TotalSize);
+  OS.write(CUOffsetsData.data(), CUOffsetsData.size());
+  // No local TUs, no foreign TUs, no hash lookups table.
+  OS.write(NamesTableData.data(), NamesTableData.size());
+  OS.write(AbbrevData.data(), AbbrevData.size());
+  OS.write(PoolInfo->PoolData.data(), PoolInfo->PoolData.size());
+
+  return Error::success();
+}
+
 static Error checkOperandCount(StringRef EncodingString,
                                ArrayRef<yaml::Hex64> Values,
                                uint64_t ExpectedOperands) {
@@ -1024,6 +1209,7 @@ DWARFYAML::getDWARFEmitterByName(StringRef SecName) {
           .Case("debug_rnglists", DWARFYAML::emitDebugRnglists)
           .Case("debug_str", DWARFYAML::emitDebugStr)
           .Case("debug_str_offsets", DWARFYAML::emitDebugStrOffsets)
+          .Case("debug_names", DWARFYAML::emitDebugNames)
           .Default([&](raw_ostream &, const DWARFYAML::Data &) {
             return createStringError(errc::not_supported,
                                      SecName + " is not supported");
diff --git a/llvm/lib/ObjectYAML/DWARFYAML.cpp b/llvm/lib/ObjectYAML/DWARFYAML.cpp
index 2bddeed4641353..5207671f57d930 100644
--- a/llvm/lib/ObjectYAML/DWARFYAML.cpp
+++ b/llvm/lib/ObjectYAML/DWARFYAML.cpp
@@ -52,6 +52,8 @@ SetVector<StringRef> DWARFYAML::Data::getNonEmptySectionNames() const {
     SecNames.insert("debug_rnglists");
   if (DebugLoclists)
     SecNames.insert("debug_loclists");
+  if (DebugNames)
+    SecNames.insert("debug_names");
   return SecNames;
 }
 
@@ -105,6 +107,7 @@ void MappingTraits<DWARFYAML::Data>::mapping(IO &IO, DWARFYAML::Data &DWARF) {
   IO.mapOptional("debug_str_offsets", DWARF.DebugStrOffsets);
   IO.mapOptional("debug_rnglists", DWARF.DebugRnglists);
   IO.mapOptional("debug_loclists", DWARF.DebugLoclists);
+  IO.mapOptional("debug_names", DWARF.DebugNames);
   IO.setContext(OldContext);
 }
 
@@ -122,6 +125,32 @@ void MappingTraits<DWARFYAML::Abbrev>::mapping(IO &IO,
   IO.mapOptional("Attributes", Abbrev.Attributes);
 }
 
+void MappingTraits<DWARFYAML::IdxForm>::mapping(IO &IO,
+                                                DWARFYAML::IdxForm &IdxForm) {
+  IO.mapRequired("Idx", IdxForm.Idx);
+  IO.mapRequired("Form", IdxForm.Form);
+}
+
+void MappingTraits<DWARFYAML::DebugNameAbbreviation>::mapping(
+    IO &IO, DWARFYAML::DebugNameAbbreviation &DebugNameAbbreviation) {
+  IO.mapRequired("Code", DebugNameAbbreviation.Code);
+  IO.mapRequired("Tag", DebugNameAbbreviation.Tag);
+  IO.mapRequired("Indices", DebugNameAbbreviation.Indices);
+}
+
+void MappingTraits<DWARFYAML::DebugNameEntry>::mapping(
+    IO &IO, DWARFYAML::DebugNameEntry &DebugNameEntry) {
+  IO.mapRequired("Name", DebugNameEntry.NameStrp);
+  IO.mapRequired("Code", DebugNameEntry.Code);
+  IO.mapOptional("Values", DebugNameEntry.Values);
+}
+
+void MappingTraits<DWARFYAML::DebugNamesSection>::mapping(
+    IO &IO, DWARFYAML::DebugNamesSection &DebugNames) {
+  IO.mapRequired("Abbreviations", DebugNames.Abbrevs);
+  IO.mapRequired("Entries", DebugNames.Entries);
+}
+
 void MappingTraits<DWARFYAML::AttributeAbbrev>::mapping(
     IO &IO, DWARFYAML::AttributeAbbrev &AttAbbrev) {
   IO.mapRequired("Attribute", AttAbbrev.Attribute);
diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp
index 38f3e076ace213..404bddebb4acdc 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/ObjectYAML/DWARFEmitter.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
 
@@ -46,4 +47,164 @@ TEST(DWARFDebugNames, TooSmallForDWARF64) {
                         "data at offset 0x2b while reading [0x28, 0x2c)"));
 }
 
+TEST(DWARFDebugNames, BasicTestEntries) {
+  const char *Yamldata = R"(
+--- !ELF
+  debug_str:
+    - 'NameType1'
+    - 'NameType2'
+
+  debug_names:
+    Abbreviations:
+    - Code:   0x1
+      Tag: DW_TAG_namespace
+      Indices:
+        - Idx:   DW_IDX_compile_unit
+          Form:  DW_FORM_data4
+        - Idx:   DW_IDX_die_offset
+          Form:  DW_FORM_ref4
+    Entries:
+    - Name:   0x0  # strp to NameType1
+      Code:   0x1
+      Values:
+        - 0x0      # Compile unit
+        - 0x0      # DIE Offset
+    - Name:   0xa  # strp to NameType2
+      Code:   0x1
+      Values:
+        - 0x1      # Compile unit
+        - 0x1      # DIE Offset
+    - Name:   0x0  # strp to NameType1
+      Code:   0x1
+      Values:
+        - 0x2     # Compile unit
+        - 0x2     # DIE Offset
+
+)";
+
+  Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
+      DWARFYAML::emitDebugSections(StringRef(Yamldata),
+                                   /*IsLittleEndian=*/true,
+                                   /*Is64BitAddrSize=*/true);
+  ASSERT_THAT_EXPECTED(Sections, Succeeded());
+  auto Ctx = DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
+  const DWARFDebugNames &DebugNames = Ctx->getDebugNames();
+  ASSERT_NE(DebugNames.begin(), DebugNames.end());
+  const auto &NameIndex = *DebugNames.begin();
+
+  ASSERT_EQ(NameIndex.getNameCount(), 2u);
+  ASSERT_EQ(NameIndex.getBucketCount(), 0u);
+  ASSERT_EQ(NameIndex.getCUCount(), 1u);
+  ASSERT_EQ(NameIndex.getCUOffset(0), 0u);
+  ASSERT_EQ(NameIndex.getForeignTUCount(), 0u);
+  ASSERT_EQ(NameIndex.getLocalTUCount(), 0u);
+
+  // Check "NameEntries": there should be one per unique name.
+  // These are indexed starting on 1.
+  auto FirstEntry = NameIndex.getNameTableEntry(1);
+  ASSERT_EQ(FirstEntry.getString(), StringRef("NameType1"));
+  auto SecondEntry = NameIndex.getNameTableEntry(2);
+  ASSERT_EQ(SecondEntry.getString(), StringRef("NameType2"));
+
+  auto FirstNameEntries = llvm::to_vector_of<DWARFDebugNames::Entry>(
+      NameIndex.equal_range("NameType1"));
+  ASSERT_EQ(FirstNameEntries.size(), 2u);
+  ASSERT_EQ(FirstNameEntries[0].getCUIndex(), 0u);
+  ASSERT_EQ(FirstNameEntries[1].getCUIndex(), 0x2);
+  ASSERT_EQ(FirstNameEntries[0].getDIEUnitOffset(), 0x0);
+  ASSERT_EQ(FirstNameEntries[1].getDIEUnitOffset(), 0x2);
+
+  auto SecondNameEntries = llvm::to_vector_of<DWARFDebugNames::Entry>(
+      NameIndex.equal_range("NameType2"));
+  ASSERT_EQ(SecondNameEntries.size(), 1u);
+  ASSERT_EQ(SecondNameEntries[0].getCUIndex(), 0x1);
+  ASSERT_EQ(SecondNameEntries[0].getDIEUnitOffset(), 0x1);
+}
+
+TEST(DWARFDebugNames, ParentEntries) {
+  const char *Yamldata = R"(
+--- !ELF
+  debug_str:
+    - 'Name1'
+    - 'Name2'
+    - 'Name3'
+    - 'Name4'
+  debug_names:
+    Abbreviations:
+    - Code:   0x11
+      Tag: DW_TAG_namespace
+      Indices:
+        - Idx:   DW_IDX_parent
+          Form:  DW_FORM_flag_present
+        - Idx:   DW_IDX_die_offset
+          Form:  DW_FORM_ref4
+    - Code:   0x22
+      Tag: DW_TAG_namespace
+      Indices:
+        - Idx:   DW_IDX_parent
+          Form:  DW_FORM_ref4
+        - Idx:   DW_IDX_die_offset
+          Form:  DW_FORM_ref4
+    - Code:   0x33
+      Tag: DW_TAG_namespace
+      Indices:
+        - Idx:   DW_IDX_die_offset
+          Form:  DW_FORM_ref4
+    Entries:
+    - Name:   0x0  # strp to Name1
+      Code:   0x11
+      Values:
+        - 0x0      # Die offset
+    - Name:   0x6  # strp to Name2
+      Code:   0x22
+      Values:
+        - 0x0      # Parent = First entry
+        - 0x1      # Die offset
+    - Name:   0xc  # strp to Name3
+      Code:   0x22
+      Values:
+        - 0x6      # Parent = Second entry
+        - 0x1      # Die offset
+    - Name:   0x12  # strp to Name4
+      Code:   0x33
+      Values:
+        - 0x1      # Die offset
+)";
+
+  Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
+      DWARFYAML::emitDebugSections(StringRef(Yamldata),
+                                   /*IsLittleEndian=*/true,
+                                   /*Is64BitAddrSize=*/true);
+  ASSERT_THAT_EXPECTED(Sections, Succeeded());
+  auto Ctx = DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
+  const DWARFDebugNames &DebugNames = Ctx->getDebugNames();
+  ASSERT_NE(DebugNames.begin(), DebugNames.end());
+  const auto &NameIndex = *DebugNames.begin();
+
+  auto Name1Entries = llvm::to_vector_of<DWARFDebugNames::Entry>(
+      NameIndex.equal_range("Name1"));
+  ASSERT_EQ(Name1Entries.size(), 1u);
+  auto Name1Parent = Name1Entries[0].getParentDIEEntry();
+  ASSERT_THAT_EXPECTED(Name1Parent, Succeeded());
+  ASSERT_EQ(*Name1Parent, std::nullopt); // Name1 has no parent
+
+  auto Name2Entries = llvm::to_vector_of<DWARFDebugNames::Entry>(
+      NameIndex.equal_range("Name2"));
+  ASSERT_EQ(Name2Entries.size(), 1u);
+  auto Name2Parent = Name2Entries[0].getParentDIEEntry();
+  ASSERT_THAT_EXPECTED(Name2Parent, Succeeded());
+  ASSERT_EQ((**Name2Parent).getDIEUnitOffset(), 0x0); // Name2 parent == Name1
+
+  auto Name3Entries = llvm::to_vector_of<DWARFDebugNames::Entry>(
+      NameIndex.equal_range("Name3"));
+  ASSERT_EQ(Name3Entries.size(), 1u);
+  auto Name3Parent = Name3Entries[0].getParentDIEEntry();
+  ASSERT_THAT_EXPECTED(Name3Parent, Succeeded());
+  ASSERT_EQ((**Name3Parent).getDIEUnitOffset(), 0x1); // Name3 parent == Name2
+
+  auto Name4Entries = llvm::to_vector_of<DWARFDebugNames::Entry>(
+      NameIndex.equal_range("Name4"));
+  ASSERT_EQ(Name4Entries.size(), 1u);
+  ASSERT_FALSE(Name4Entries[0].hasParentInformation());
+}
 } // end anonymous namespace

>From 842f4caa629955a6359d0fdbf0cbf427792c5202 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Mon, 5 Feb 2024 10:17:15 -0800
Subject: [PATCH 2/2] fixup! address review comments

---
 llvm/include/llvm/ObjectYAML/DWARFYAML.h      | 40 ++++-----
 llvm/lib/ObjectYAML/DWARFEmitter.cpp          | 87 ++++++++++---------
 .../DWARF/DWARFAcceleratorTableTest.cpp       | 45 +++++-----
 3 files changed, 89 insertions(+), 83 deletions(-)

diff --git a/llvm/include/llvm/ObjectYAML/DWARFYAML.h b/llvm/include/llvm/ObjectYAML/DWARFYAML.h
index 5d1213d9ff965b..0b3bea786d31c7 100644
--- a/llvm/include/llvm/ObjectYAML/DWARFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/DWARFYAML.h
@@ -119,20 +119,20 @@ struct Unit {
 };
 
 struct IdxForm {
-  llvm::dwarf::Index Idx;
-  llvm::dwarf::Form Form;
+  dwarf::Index Idx;
+  dwarf::Form Form;
 };
 
 struct DebugNameAbbreviation {
-  llvm::yaml::Hex64 Code;
-  llvm::dwarf::Tag Tag;
+  yaml::Hex64 Code;
+  dwarf::Tag Tag;
   std::vector<IdxForm> Indices;
 };
 
 struct DebugNameEntry {
-  llvm::yaml::Hex32 NameStrp;
-  llvm::yaml::Hex64 Code;
-  std::vector<llvm::yaml::Hex64> Values;
+  yaml::Hex32 NameStrp;
+  yaml::Hex64 Code;
+  std::vector<yaml::Hex64> Values;
 };
 
 struct DebugNamesSection {
@@ -306,19 +306,6 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::IdxForm)
 namespace llvm {
 namespace yaml {
 
-template <> struct MappingTraits<DWARFYAML::DebugNamesSection> {
-  static void mapping(IO &IO, DWARFYAML::DebugNamesSection &);
-};
-template <> struct MappingTraits<DWARFYAML::DebugNameEntry> {
-  static void mapping(IO &IO, DWARFYAML::DebugNameEntry &);
-};
-template <> struct MappingTraits<DWARFYAML::DebugNameAbbreviation> {
-  static void mapping(IO &IO, DWARFYAML::DebugNameAbbreviation &);
-};
-template <> struct MappingTraits<DWARFYAML::IdxForm> {
-  static void mapping(IO &IO, DWARFYAML::IdxForm &);
-};
-
 template <> struct MappingTraits<DWARFYAML::Data> {
   static void mapping(IO &IO, DWARFYAML::Data &DWARF);
 };
@@ -363,6 +350,19 @@ template <> struct MappingTraits<DWARFYAML::Unit> {
   static void mapping(IO &IO, DWARFYAML::Unit &Unit);
 };
 
+template <> struct MappingTraits<DWARFYAML::DebugNamesSection> {
+  static void mapping(IO &IO, DWARFYAML::DebugNamesSection &);
+};
+template <> struct MappingTraits<DWARFYAML::DebugNameEntry> {
+  static void mapping(IO &IO, DWARFYAML::DebugNameEntry &);
+};
+template <> struct MappingTraits<DWARFYAML::DebugNameAbbreviation> {
+  static void mapping(IO &IO, DWARFYAML::DebugNameAbbreviation &);
+};
+template <> struct MappingTraits<DWARFYAML::IdxForm> {
+  static void mapping(IO &IO, DWARFYAML::IdxForm &);
+};
+
 template <> struct MappingTraits<DWARFYAML::Entry> {
   static void mapping(IO &IO, DWARFYAML::Entry &Entry);
 };
diff --git a/llvm/lib/ObjectYAML/DWARFEmitter.cpp b/llvm/lib/ObjectYAML/DWARFEmitter.cpp
index bf01ade9156dbe..9b2dae5c3e0415 100644
--- a/llvm/lib/ObjectYAML/DWARFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/DWARFEmitter.cpp
@@ -696,10 +696,11 @@ namespace {
 void emitDebugNamesHeader(raw_ostream &OS, bool IsLittleEndian,
                           uint32_t NameCount, uint32_t AbbrevSize,
                           uint32_t CombinedSizeOtherParts) {
+  // Use the same AugmentationString as AsmPrinter.
   StringRef AugmentationString = "LLVM0700";
-  auto TotalSize = CombinedSizeOtherParts + 5 * sizeof(uint32_t) +
-                   2 * sizeof(uint16_t) + sizeof(NameCount) +
-                   sizeof(AbbrevSize) + AugmentationString.size();
+  size_t TotalSize = CombinedSizeOtherParts + 5 * sizeof(uint32_t) +
+                     2 * sizeof(uint16_t) + sizeof(NameCount) +
+                     sizeof(AbbrevSize) + AugmentationString.size();
   writeInteger(uint32_t(TotalSize), OS, IsLittleEndian); // Unit length
 
   // Everything below is included in total size.
@@ -720,8 +721,8 @@ void emitDebugNamesHeader(raw_ostream &OS, bool IsLittleEndian,
 std::string
 emitDebugNamesAbbrev(ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
   std::string Data;
-  llvm::raw_string_ostream OS(Data);
-  for (const auto &Abbrev : Abbrevs) {
+  raw_string_ostream OS(Data);
+  for (const DWARFYAML::DebugNameAbbreviation &Abbrev : Abbrevs) {
     encodeULEB128(Abbrev.Code, OS);
     encodeULEB128(Abbrev.Tag, OS);
     for (auto [Idx, Form] : Abbrev.Indices) {
@@ -739,7 +740,7 @@ emitDebugNamesAbbrev(ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
 /// CU at offset 0.
 std::string emitDebugNamesCUOffsets(bool IsLittleEndian) {
   std::string Data;
-  llvm::raw_string_ostream OS(Data);
+  raw_string_ostream OS(Data);
   writeInteger(uint32_t(0), OS, IsLittleEndian);
   return Data;
 }
@@ -750,27 +751,27 @@ std::string emitDebugNamesCUOffsets(bool IsLittleEndian) {
 /// provided in `Entries`.
 std::string emitDebugNamesNameTable(
     bool IsLittleEndian,
-    const std::map<uint32_t, std::vector<DWARFYAML::DebugNameEntry>> &Entries,
+    const DenseMap<uint32_t, std::vector<DWARFYAML::DebugNameEntry>> &Entries,
     ArrayRef<uint32_t> EntryPoolOffsets) {
   assert(Entries.size() == EntryPoolOffsets.size());
 
   std::string Data;
-  llvm::raw_string_ostream OS(Data);
+  raw_string_ostream OS(Data);
 
-  for (auto Strp : make_first_range(Entries))
+  for (uint32_t Strp : make_first_range(Entries))
     writeInteger(Strp, OS, IsLittleEndian);
-  for (auto PoolOffset : EntryPoolOffsets)
-    writeInteger(uint32_t(PoolOffset), OS, IsLittleEndian);
+  for (uint32_t PoolOffset : EntryPoolOffsets)
+    writeInteger(PoolOffset, OS, IsLittleEndian);
   return Data;
 }
 
-/// Groups entries based on their name (strp) code and returns a sorted map.
-std::map<uint32_t, std::vector<DWARFYAML::DebugNameEntry>>
+/// Groups entries based on their name (strp) code and returns a map.
+DenseMap<uint32_t, std::vector<DWARFYAML::DebugNameEntry>>
 groupEntries(ArrayRef<DWARFYAML::DebugNameEntry> Entries) {
-  std::map<uint32_t, std::vector<DWARFYAML::DebugNameEntry>> Ans;
-  for (const auto &Entry : Entries)
-    Ans[Entry.NameStrp].push_back(Entry);
-  return Ans;
+  DenseMap<uint32_t, std::vector<DWARFYAML::DebugNameEntry>> StrpToEntries;
+  for (const DWARFYAML::DebugNameEntry &Entry : Entries)
+    StrpToEntries[Entry.NameStrp].push_back(Entry);
+  return StrpToEntries;
 }
 
 /// Finds the abbreviation whose code is AbbrevCode and returns a list
@@ -783,15 +784,15 @@ getNonZeroDataSizesFor(uint32_t AbbrevCode,
   });
   if (AbbrevIt == Abbrevs.end())
     return createStringError(inconvertibleErrorCode(),
-                             "Did not find an Abbreviation for this code");
+                             "did not find an Abbreviation for this code");
 
   SmallVector<uint8_t> DataSizes;
-  dwarf::FormParams Params{5 /*Version*/, 4 /*AddrSize*/, dwarf::DWARF32};
+  dwarf::FormParams Params{/*Version*/ 5, /*AddrSize*/ 4, dwarf::DWARF32};
   for (auto [Idx, Form] : AbbrevIt->Indices) {
-    auto FormSize = dwarf::getFixedFormByteSize(Form, Params);
+    std::optional<uint8_t> FormSize = dwarf::getFixedFormByteSize(Form, Params);
     if (FormSize == std::nullopt)
       return createStringError(inconvertibleErrorCode(),
-                               "Unsupported Form for YAML debug_names emitter");
+                               "unsupported Form for YAML debug_names emitter");
     if (FormSize == 0)
       continue;
     DataSizes.push_back(*FormSize);
@@ -810,62 +811,64 @@ struct PoolOffsetsAndData {
 /// the abbreviation table.
 Expected<PoolOffsetsAndData> emitDebugNamesEntryPool(
     bool IsLittleEndian,
-    const std::map<uint32_t, std::vector<DWARFYAML::DebugNameEntry>>
-        &SortedEntries,
+    const DenseMap<uint32_t, std::vector<DWARFYAML::DebugNameEntry>>
+        &StrpToEntries,
     ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
-  PoolOffsetsAndData Ans;
-  llvm::raw_string_ostream OS(Ans.PoolData);
+  PoolOffsetsAndData Result;
+  raw_string_ostream OS(Result.PoolData);
 
   for (ArrayRef<DWARFYAML::DebugNameEntry> EntriesWithSameName :
-       llvm::make_second_range(SortedEntries)) {
-    Ans.PoolOffsets.push_back(Ans.PoolData.size());
+       make_second_range(StrpToEntries)) {
+    Result.PoolOffsets.push_back(Result.PoolData.size());
 
-    for (const auto &Entry : EntriesWithSameName) {
+    for (const DWARFYAML::DebugNameEntry &Entry : EntriesWithSameName) {
       encodeULEB128(Entry.Code, OS);
 
-      auto DataSizes = getNonZeroDataSizesFor(Entry.Code, Abbrevs);
+      Expected<SmallVector<uint8_t>> DataSizes =
+          getNonZeroDataSizesFor(Entry.Code, Abbrevs);
       if (!DataSizes)
         return DataSizes.takeError();
       if (DataSizes->size() != Entry.Values.size())
         return createStringError(
             inconvertibleErrorCode(),
-            "Mismatch between provided and required number of values");
+            "mismatch between provided and required number of values");
 
       for (auto [Value, ValueSize] : zip_equal(Entry.Values, *DataSizes))
-        if (auto E =
+        if (Error E =
                 writeVariableSizedInteger(Value, ValueSize, OS, IsLittleEndian))
           return std::move(E);
     }
     encodeULEB128(0, OS);
   }
 
-  return Ans;
+  return Result;
 }
 } // namespace
 
 Error DWARFYAML::emitDebugNames(raw_ostream &OS, const Data &DI) {
   assert(DI.DebugNames && "unexpected emitDebugNames() call");
-  const auto DebugNames = DI.DebugNames.value();
+  const DebugNamesSection DebugNames = DI.DebugNames.value();
 
-  auto SortedEntries = groupEntries(DebugNames.Entries);
+  DenseMap<uint32_t, std::vector<DebugNameEntry>> StrpToEntries =
+      groupEntries(DebugNames.Entries);
 
   // Emit all sub-sections into individual strings so that we may compute
   // relative offsets and sizes.
   Expected<PoolOffsetsAndData> PoolInfo = emitDebugNamesEntryPool(
-      DI.IsLittleEndian, SortedEntries, DebugNames.Abbrevs);
+      DI.IsLittleEndian, StrpToEntries, DebugNames.Abbrevs);
   if (!PoolInfo)
     return PoolInfo.takeError();
-  auto NamesTableData = emitDebugNamesNameTable(
-      DI.IsLittleEndian, SortedEntries, PoolInfo->PoolOffsets);
+  std::string NamesTableData = emitDebugNamesNameTable(
+      DI.IsLittleEndian, StrpToEntries, PoolInfo->PoolOffsets);
 
-  auto AbbrevData = emitDebugNamesAbbrev(DebugNames.Abbrevs);
-  auto CUOffsetsData = emitDebugNamesCUOffsets(DI.IsLittleEndian);
+  std::string AbbrevData = emitDebugNamesAbbrev(DebugNames.Abbrevs);
+  std::string CUOffsetsData = emitDebugNamesCUOffsets(DI.IsLittleEndian);
 
-  auto TotalSize = PoolInfo->PoolData.size() + NamesTableData.size() +
-                   AbbrevData.size() + CUOffsetsData.size();
+  size_t TotalSize = PoolInfo->PoolData.size() + NamesTableData.size() +
+                     AbbrevData.size() + CUOffsetsData.size();
 
   // Start real emission by combining all individual strings.
-  emitDebugNamesHeader(OS, DI.IsLittleEndian, SortedEntries.size(),
+  emitDebugNamesHeader(OS, DI.IsLittleEndian, StrpToEntries.size(),
                        AbbrevData.size(), TotalSize);
   OS.write(CUOffsetsData.data(), CUOffsetsData.size());
   // No local TUs, no foreign TUs, no hash lookups table.
diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp
index 404bddebb4acdc..b90c47b7b81fa0 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFAcceleratorTableTest.cpp
@@ -83,14 +83,14 @@ TEST(DWARFDebugNames, BasicTestEntries) {
 )";
 
   Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
-      DWARFYAML::emitDebugSections(StringRef(Yamldata),
+      DWARFYAML::emitDebugSections(Yamldata,
                                    /*IsLittleEndian=*/true,
                                    /*Is64BitAddrSize=*/true);
   ASSERT_THAT_EXPECTED(Sections, Succeeded());
   auto Ctx = DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
   const DWARFDebugNames &DebugNames = Ctx->getDebugNames();
   ASSERT_NE(DebugNames.begin(), DebugNames.end());
-  const auto &NameIndex = *DebugNames.begin();
+  const DWARFDebugNames::NameIndex &NameIndex = *DebugNames.begin();
 
   ASSERT_EQ(NameIndex.getNameCount(), 2u);
   ASSERT_EQ(NameIndex.getBucketCount(), 0u);
@@ -101,21 +101,21 @@ TEST(DWARFDebugNames, BasicTestEntries) {
 
   // Check "NameEntries": there should be one per unique name.
   // These are indexed starting on 1.
-  auto FirstEntry = NameIndex.getNameTableEntry(1);
+  DWARFDebugNames::NameTableEntry FirstEntry = NameIndex.getNameTableEntry(1);
   ASSERT_EQ(FirstEntry.getString(), StringRef("NameType1"));
-  auto SecondEntry = NameIndex.getNameTableEntry(2);
+  DWARFDebugNames::NameTableEntry SecondEntry = NameIndex.getNameTableEntry(2);
   ASSERT_EQ(SecondEntry.getString(), StringRef("NameType2"));
 
-  auto FirstNameEntries = llvm::to_vector_of<DWARFDebugNames::Entry>(
-      NameIndex.equal_range("NameType1"));
+  SmallVector<DWARFDebugNames::Entry> FirstNameEntries =
+      to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("NameType1"));
   ASSERT_EQ(FirstNameEntries.size(), 2u);
   ASSERT_EQ(FirstNameEntries[0].getCUIndex(), 0u);
   ASSERT_EQ(FirstNameEntries[1].getCUIndex(), 0x2);
   ASSERT_EQ(FirstNameEntries[0].getDIEUnitOffset(), 0x0);
   ASSERT_EQ(FirstNameEntries[1].getDIEUnitOffset(), 0x2);
 
-  auto SecondNameEntries = llvm::to_vector_of<DWARFDebugNames::Entry>(
-      NameIndex.equal_range("NameType2"));
+  SmallVector<DWARFDebugNames::Entry> SecondNameEntries =
+      to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("NameType2"));
   ASSERT_EQ(SecondNameEntries.size(), 1u);
   ASSERT_EQ(SecondNameEntries[0].getCUIndex(), 0x1);
   ASSERT_EQ(SecondNameEntries[0].getDIEUnitOffset(), 0x1);
@@ -172,38 +172,41 @@ TEST(DWARFDebugNames, ParentEntries) {
 )";
 
   Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
-      DWARFYAML::emitDebugSections(StringRef(Yamldata),
+      DWARFYAML::emitDebugSections(Yamldata,
                                    /*IsLittleEndian=*/true,
                                    /*Is64BitAddrSize=*/true);
   ASSERT_THAT_EXPECTED(Sections, Succeeded());
   auto Ctx = DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
   const DWARFDebugNames &DebugNames = Ctx->getDebugNames();
   ASSERT_NE(DebugNames.begin(), DebugNames.end());
-  const auto &NameIndex = *DebugNames.begin();
+  const DWARFDebugNames::NameIndex &NameIndex = *DebugNames.begin();
 
-  auto Name1Entries = llvm::to_vector_of<DWARFDebugNames::Entry>(
-      NameIndex.equal_range("Name1"));
+  SmallVector<DWARFDebugNames::Entry> Name1Entries =
+      to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("Name1"));
   ASSERT_EQ(Name1Entries.size(), 1u);
-  auto Name1Parent = Name1Entries[0].getParentDIEEntry();
+  Expected<std::optional<DWARFDebugNames::Entry>> Name1Parent =
+      Name1Entries[0].getParentDIEEntry();
   ASSERT_THAT_EXPECTED(Name1Parent, Succeeded());
   ASSERT_EQ(*Name1Parent, std::nullopt); // Name1 has no parent
 
-  auto Name2Entries = llvm::to_vector_of<DWARFDebugNames::Entry>(
-      NameIndex.equal_range("Name2"));
+  SmallVector<DWARFDebugNames::Entry> Name2Entries =
+      to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("Name2"));
   ASSERT_EQ(Name2Entries.size(), 1u);
-  auto Name2Parent = Name2Entries[0].getParentDIEEntry();
+  Expected<std::optional<DWARFDebugNames::Entry>> Name2Parent =
+      Name2Entries[0].getParentDIEEntry();
   ASSERT_THAT_EXPECTED(Name2Parent, Succeeded());
   ASSERT_EQ((**Name2Parent).getDIEUnitOffset(), 0x0); // Name2 parent == Name1
 
-  auto Name3Entries = llvm::to_vector_of<DWARFDebugNames::Entry>(
-      NameIndex.equal_range("Name3"));
+  SmallVector<DWARFDebugNames::Entry> Name3Entries =
+      to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("Name3"));
   ASSERT_EQ(Name3Entries.size(), 1u);
-  auto Name3Parent = Name3Entries[0].getParentDIEEntry();
+  Expected<std::optional<DWARFDebugNames::Entry>> Name3Parent =
+      Name3Entries[0].getParentDIEEntry();
   ASSERT_THAT_EXPECTED(Name3Parent, Succeeded());
   ASSERT_EQ((**Name3Parent).getDIEUnitOffset(), 0x1); // Name3 parent == Name2
 
-  auto Name4Entries = llvm::to_vector_of<DWARFDebugNames::Entry>(
-      NameIndex.equal_range("Name4"));
+  SmallVector<DWARFDebugNames::Entry> Name4Entries =
+      to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("Name4"));
   ASSERT_EQ(Name4Entries.size(), 1u);
   ASSERT_FALSE(Name4Entries[0].hasParentInformation());
 }



More information about the llvm-commits mailing list