[lldb] Use LLVM's implementation of AppleTables for apple_debug_types
authorFelipe de Azevedo Piovezan <fpiovezan@apple.com>
Tue, 20 Jun 2023 16:21:38 +0000 (12:21 -0400)
committerFelipe de Azevedo Piovezan <fpiovezan@apple.com>
Wed, 28 Jun 2023 13:19:14 +0000 (09:19 -0400)
This commit is replacing really old LLDB code, and we've found some odd
behavior while doing this replacement. While the changes here are largely NFC,
there are some subtle changes that fix such odd behavior.

The most curious example of this is the method `FindCompleteObjCClassName`,
which has a flag `must_be_implementation`. This flag was _only_ being respected
for accelerator tables containing the atom `type_flags`, which seems
counter-intuitive. The implementation for DWARF 5 tables does not do that and
neither does the code introduced in this patch.

There were other weird cases, for example, we found boolean logic that was
always true in a code path: look for a `if  !has_qualified_name...` deleted
line; that condition was true by simple if/else analysis.

Differential Revision: https://reviews.llvm.org/D153867

lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp
lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h
llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h

index 5cc44ea..0cd8b3d 100644 (file)
@@ -32,10 +32,8 @@ std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
       std::make_unique<llvm::AppleAcceleratorTable>(
           apple_namespaces.GetAsLLVMDWARF(), llvm_debug_str);
 
-  auto apple_types_table_up = std::make_unique<DWARFMappedHash::MemoryTable>(
-      apple_types, debug_str, ".apple_types");
-  if (!apple_types_table_up->IsValid())
-    apple_types_table_up.reset();
+  auto apple_types_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
+      apple_types.GetAsLLVMDWARF(), llvm_debug_str);
 
   auto apple_objc_table_up = std::make_unique<DWARFMappedHash::MemoryTable>(
       apple_objc, debug_str, ".apple_objc");
@@ -51,6 +49,7 @@ std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
 
   extract_and_check(apple_names_table_up);
   extract_and_check(apple_namespaces_table_up);
+  extract_and_check(apple_types_table_up);
 
   if (apple_names_table_up || apple_namespaces_table_up ||
       apple_types_table_up || apple_objc_table_up)
@@ -62,16 +61,69 @@ std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
   return nullptr;
 }
 
+/// Returns true if `tag` is a class_type of structure_type tag.
+static bool IsClassOrStruct(dw_tag_t tag) {
+  return tag == DW_TAG_class_type || tag == DW_TAG_structure_type;
+}
+
+/// Returns true if `entry` has an extractable DW_ATOM_qual_name_hash and it
+/// matches `expected_hash`.
+static bool
+EntryHasMatchingQualhash(const llvm::AppleAcceleratorTable::Entry &entry,
+                         uint32_t expected_hash) {
+  std::optional<llvm::DWARFFormValue> form_value =
+      entry.lookup(dwarf::DW_ATOM_qual_name_hash);
+  if (!form_value)
+    return false;
+  std::optional<uint64_t> hash = form_value->getAsUnsignedConstant();
+  return hash && (*hash == expected_hash);
+}
+
+/// Returns true if `entry` has an extractable DW_ATOM_die_tag and it matches
+/// `expected_tag`. We also consider it a match if the tags are different but
+/// in the set of {TAG_class_type, TAG_struct_type}.
+static bool EntryHasMatchingTag(const llvm::AppleAcceleratorTable::Entry &entry,
+                                dw_tag_t expected_tag) {
+  std::optional<llvm::DWARFFormValue> form_value =
+      entry.lookup(dwarf::DW_ATOM_die_tag);
+  if (!form_value)
+    return false;
+  std::optional<uint64_t> maybe_tag = form_value->getAsUnsignedConstant();
+  if (!maybe_tag)
+    return false;
+  auto tag = static_cast<dw_tag_t>(*maybe_tag);
+  return tag == expected_tag ||
+         (IsClassOrStruct(tag) && IsClassOrStruct(expected_tag));
+}
+
+/// Returns true if `entry` has an extractable DW_ATOM_type_flags and the flag
+/// "DW_FLAG_type_implementation" is set.
+static bool
+HasImplementationFlag(const llvm::AppleAcceleratorTable::Entry &entry) {
+  std::optional<llvm::DWARFFormValue> form_value =
+      entry.lookup(dwarf::DW_ATOM_type_flags);
+  if (!form_value)
+    return false;
+  std::optional<uint64_t> Flags = form_value->getAsUnsignedConstant();
+  return Flags &&
+         (*Flags & llvm::dwarf::AcceleratorTable::DW_FLAG_type_implementation);
+}
+
 void AppleDWARFIndex::SearchFor(const llvm::AppleAcceleratorTable &table,
                                 llvm::StringRef name,
                                 llvm::function_ref<bool(DWARFDIE die)> callback,
                                 std::optional<dw_tag_t> search_for_tag,
                                 std::optional<uint32_t> search_for_qualhash) {
-  assert(!search_for_tag && !search_for_qualhash && "not yet implemented");
   auto converted_cb = DIERefCallback(callback, name);
-  for (const auto &entry : table.equal_range(name))
+  for (const auto &entry : table.equal_range(name)) {
+    if (search_for_qualhash &&
+        !EntryHasMatchingQualhash(entry, *search_for_qualhash))
+      continue;
+    if (search_for_tag && !EntryHasMatchingTag(entry, *search_for_tag))
+      continue;
     if (!converted_cb(entry))
       break;
+  }
 }
 
 void AppleDWARFIndex::GetGlobalVariables(
@@ -130,18 +182,32 @@ void AppleDWARFIndex::GetCompleteObjCClass(
     llvm::function_ref<bool(DWARFDIE die)> callback) {
   if (!m_apple_types_up)
     return;
-  m_apple_types_up->FindCompleteObjCClassByName(
-      class_name.GetStringRef(),
-      DIERefCallback(callback, class_name.GetStringRef()),
-      must_be_implementation);
+
+  llvm::SmallVector<DIERef> decl_dies;
+  auto converted_cb = DIERefCallback(callback, class_name);
+
+  for (const auto &entry : m_apple_types_up->equal_range(class_name)) {
+    if (HasImplementationFlag(entry)) {
+      converted_cb(entry);
+      return;
+    }
+
+    decl_dies.emplace_back(std::nullopt, DIERef::Section::DebugInfo,
+                           *entry.getDIESectionOffset());
+  }
+
+  if (must_be_implementation)
+    return;
+  for (DIERef ref : decl_dies)
+    if (!converted_cb(ref))
+      return;
 }
 
 void AppleDWARFIndex::GetTypes(
     ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
   if (!m_apple_types_up)
     return;
-  m_apple_types_up->FindByName(name.GetStringRef(),
-                               DIERefCallback(callback, name.GetStringRef()));
+  SearchFor(*m_apple_types_up, name, callback);
 }
 
 void AppleDWARFIndex::GetTypes(
@@ -151,51 +217,47 @@ void AppleDWARFIndex::GetTypes(
     return;
 
   Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
-  const bool has_tag = m_apple_types_up->GetHeader().header_data.ContainsAtom(
-      DWARFMappedHash::eAtomTypeTag);
-  const bool has_qualified_name_hash =
-      m_apple_types_up->GetHeader().header_data.ContainsAtom(
-          DWARFMappedHash::eAtomTypeQualNameHash);
-
-  const ConstString type_name(context[0].name);
-  const dw_tag_t tag = context[0].tag;
-  if (has_tag && has_qualified_name_hash) {
-    const char *qualified_name = context.GetQualifiedName();
-    const uint32_t qualified_name_hash = llvm::djbHash(qualified_name);
+  const bool entries_have_tag =
+      m_apple_types_up->containsAtomType(DW_ATOM_die_tag);
+  const bool entries_have_qual_hash =
+      m_apple_types_up->containsAtomType(DW_ATOM_qual_name_hash);
+
+  llvm::StringRef expected_name = context[0].name;
+
+  if (entries_have_tag && entries_have_qual_hash) {
+    const dw_tag_t expected_tag = context[0].tag;
+    const uint32_t expected_qualname_hash =
+        llvm::djbHash(context.GetQualifiedName());
     if (log)
       m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
-    m_apple_types_up->FindByNameAndTagAndQualifiedNameHash(
-        type_name.GetStringRef(), tag, qualified_name_hash,
-        DIERefCallback(callback, type_name.GetStringRef()));
+    SearchFor(*m_apple_types_up, expected_name, callback, expected_tag,
+               expected_qualname_hash);
     return;
   }
 
-  if (has_tag) {
-    // When searching for a scoped type (for example,
-    // "std::vector<int>::const_iterator") searching for the innermost
-    // name alone ("const_iterator") could yield many false
-    // positives. By searching for the parent type ("vector<int>")
-    // first we can avoid extracting type DIEs from object files that
-    // would fail the filter anyway.
-    if (!has_qualified_name_hash && (context.GetSize() > 1) &&
-        (context[1].tag == DW_TAG_class_type ||
-         context[1].tag == DW_TAG_structure_type)) {
-      if (m_apple_types_up->FindByName(context[1].name,
-                                       [&](DIERef ref) { return false; }))
-        return;
-    }
-
-    if (log)
-      m_module.LogMessage(log, "FindByNameAndTag()");
-    m_apple_types_up->FindByNameAndTag(
-        type_name.GetStringRef(), tag,
-        DIERefCallback(callback, type_name.GetStringRef()));
+  // Historically, if there are no tags, we also ignore qual_hash (why?)
+  if (!entries_have_tag) {
+    SearchFor(*m_apple_names_up, expected_name, callback);
     return;
   }
 
-  m_apple_types_up->FindByName(
-      type_name.GetStringRef(),
-      DIERefCallback(callback, type_name.GetStringRef()));
+  // We have a tag but no qual hash.
+
+  // When searching for a scoped type (for example,
+  // "std::vector<int>::const_iterator") searching for the innermost
+  // name alone ("const_iterator") could yield many false
+  // positives. By searching for the parent type ("vector<int>")
+  // first we can avoid extracting type DIEs from object files that
+  // would fail the filter anyway.
+  if ((context.GetSize() > 1) && IsClassOrStruct(context[1].tag))
+    if (m_apple_types_up->equal_range(context[1].name).empty())
+      return;
+
+  if (log)
+    m_module.LogMessage(log, "FindByNameAndTag()");
+  const dw_tag_t expected_tag = context[0].tag;
+  SearchFor(*m_apple_types_up, expected_name, callback, expected_tag);
+  return;
 }
 
 void AppleDWARFIndex::GetNamespaces(
index 50a8fe2..5ff88cd 100644 (file)
@@ -24,7 +24,7 @@ public:
   AppleDWARFIndex(Module &module,
                   std::unique_ptr<llvm::AppleAcceleratorTable> apple_names,
                   std::unique_ptr<llvm::AppleAcceleratorTable> apple_namespaces,
-                  std::unique_ptr<DWARFMappedHash::MemoryTable> apple_types,
+                  std::unique_ptr<llvm::AppleAcceleratorTable> apple_types,
                   std::unique_ptr<DWARFMappedHash::MemoryTable> apple_objc)
       : DWARFIndex(module), m_apple_names_up(std::move(apple_names)),
         m_apple_namespaces_up(std::move(apple_namespaces)),
@@ -65,7 +65,7 @@ public:
 private:
   std::unique_ptr<llvm::AppleAcceleratorTable> m_apple_names_up;
   std::unique_ptr<llvm::AppleAcceleratorTable> m_apple_namespaces_up;
-  std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_types_up;
+  std::unique_ptr<llvm::AppleAcceleratorTable> m_apple_types_up;
   std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_objc_up;
 
   /// Search for entries whose name is `name` in `table`, calling `callback` for
index dee6441..19d2432 100644 (file)
@@ -23,92 +23,6 @@ bool DWARFMappedHash::ExtractDIEArray(
   return true;
 }
 
-void DWARFMappedHash::ExtractDIEArray(
-    const DIEInfoArray &die_info_array, const dw_tag_t tag,
-    llvm::function_ref<bool(DIERef ref)> callback) {
-  if (tag == 0) {
-    ExtractDIEArray(die_info_array, callback);
-    return;
-  }
-
-  const size_t count = die_info_array.size();
-  for (size_t i = 0; i < count; ++i) {
-    const dw_tag_t die_tag = die_info_array[i].tag;
-    bool tag_matches = die_tag == 0 || tag == die_tag;
-    if (!tag_matches) {
-      if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
-        tag_matches = tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
-    }
-    if (tag_matches) {
-      if (!callback(DIERef(die_info_array[i])))
-        return;
-    }
-  }
-}
-
-void DWARFMappedHash::ExtractDIEArray(
-    const DIEInfoArray &die_info_array, const dw_tag_t tag,
-    const uint32_t qualified_name_hash,
-    llvm::function_ref<bool(DIERef ref)> callback) {
-  if (tag == 0) {
-    ExtractDIEArray(die_info_array, callback);
-    return;
-  }
-
-  const size_t count = die_info_array.size();
-  for (size_t i = 0; i < count; ++i) {
-    if (qualified_name_hash != die_info_array[i].qualified_name_hash)
-      continue;
-    const dw_tag_t die_tag = die_info_array[i].tag;
-    bool tag_matches = die_tag == 0 || tag == die_tag;
-    if (!tag_matches) {
-      if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
-        tag_matches = tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
-    }
-    if (tag_matches) {
-      if (!callback(DIERef(die_info_array[i])))
-        return;
-    }
-  }
-}
-
-void DWARFMappedHash::ExtractClassOrStructDIEArray(
-    const DIEInfoArray &die_info_array,
-    bool return_implementation_only_if_available,
-    llvm::function_ref<bool(DIERef ref)> callback) {
-  const size_t count = die_info_array.size();
-  for (size_t i = 0; i < count; ++i) {
-    const dw_tag_t die_tag = die_info_array[i].tag;
-    if (!(die_tag == 0 || die_tag == DW_TAG_class_type ||
-          die_tag == DW_TAG_structure_type))
-      continue;
-    bool is_implementation =
-        (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) != 0;
-    if (is_implementation != return_implementation_only_if_available)
-      continue;
-    if (return_implementation_only_if_available) {
-      // We found the one true definition for this class, so only return
-      // that
-      callback(DIERef(die_info_array[i]));
-      return;
-    }
-    if (!callback(DIERef(die_info_array[i])))
-      return;
-  }
-}
-
-void DWARFMappedHash::ExtractTypesFromDIEArray(
-    const DIEInfoArray &die_info_array, uint32_t type_flag_mask,
-    uint32_t type_flag_value, llvm::function_ref<bool(DIERef ref)> callback) {
-  const size_t count = die_info_array.size();
-  for (size_t i = 0; i < count; ++i) {
-    if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value) {
-      if (!callback(DIERef(die_info_array[i])))
-        return;
-    }
-  }
-}
-
 const char *DWARFMappedHash::GetAtomTypeName(uint16_t atom) {
   switch (atom) {
   case eAtomTypeNULL:
@@ -414,56 +328,6 @@ bool DWARFMappedHash::MemoryTable::FindByName(
   return DWARFMappedHash::ExtractDIEArray(die_info_array, callback);
 }
 
-void DWARFMappedHash::MemoryTable::FindByNameAndTag(
-    llvm::StringRef name, const dw_tag_t tag,
-    llvm::function_ref<bool(DIERef ref)> callback) {
-  DIEInfoArray die_info_array;
-  FindByName(name, die_info_array);
-  DWARFMappedHash::ExtractDIEArray(die_info_array, tag, callback);
-}
-
-void DWARFMappedHash::MemoryTable::FindByNameAndTagAndQualifiedNameHash(
-    llvm::StringRef name, const dw_tag_t tag,
-    const uint32_t qualified_name_hash,
-    llvm::function_ref<bool(DIERef ref)> callback) {
-  DIEInfoArray die_info_array;
-  FindByName(name, die_info_array);
-  DWARFMappedHash::ExtractDIEArray(die_info_array, tag, qualified_name_hash,
-                                   callback);
-}
-
-void DWARFMappedHash::MemoryTable::FindCompleteObjCClassByName(
-    llvm::StringRef name, llvm::function_ref<bool(DIERef ref)> callback,
-    bool must_be_implementation) {
-  DIEInfoArray die_info_array;
-  FindByName(name, die_info_array);
-  if (must_be_implementation &&
-      GetHeader().header_data.ContainsAtom(eAtomTypeTypeFlags)) {
-    // If we have two atoms, then we have the DIE offset and the type flags
-    // so we can find the objective C class efficiently.
-    DWARFMappedHash::ExtractTypesFromDIEArray(
-        die_info_array, UINT32_MAX, eTypeFlagClassIsImplementation, callback);
-    return;
-  }
-  // We don't only want the one true definition, so try and see what we can
-  // find, and only return class or struct DIEs. If we do have the full
-  // implementation, then return it alone, else return all possible
-  // matches.
-  bool found_implementation = false;
-  DWARFMappedHash::ExtractClassOrStructDIEArray(
-      die_info_array, true /*return_implementation_only_if_available*/,
-      [&](DIERef ref) {
-        found_implementation = true;
-        // Here the return value does not matter as we are called at most once.
-        return callback(ref);
-      });
-  if (found_implementation)
-    return;
-  DWARFMappedHash::ExtractClassOrStructDIEArray(
-      die_info_array, false /*return_implementation_only_if_available*/,
-      callback);
-}
-
 void DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name,
                                               DIEInfoArray &die_info_array) {
   if (name.empty())
index 2d28f97..eb6d4c8 100644 (file)
@@ -135,19 +135,6 @@ public:
     bool FindByName(llvm::StringRef name,
                     llvm::function_ref<bool(DIERef ref)> callback);
 
-    void FindByNameAndTag(llvm::StringRef name, const dw_tag_t tag,
-                          llvm::function_ref<bool(DIERef ref)> callback);
-
-    void FindByNameAndTagAndQualifiedNameHash(
-        llvm::StringRef name, const dw_tag_t tag,
-        const uint32_t qualified_name_hash,
-        llvm::function_ref<bool(DIERef ref)> callback);
-
-    void
-    FindCompleteObjCClassByName(llvm::StringRef name,
-                                llvm::function_ref<bool(DIERef ref)> callback,
-                                bool must_be_implementation);
-
   protected:
     void FindByName(llvm::StringRef name, DIEInfoArray &die_info_array);
 
@@ -164,25 +151,6 @@ public:
                               llvm::function_ref<bool(DIERef ref)> callback);
 
 protected:
-  static void ExtractDIEArray(const DIEInfoArray &die_info_array,
-                              const dw_tag_t tag,
-                              llvm::function_ref<bool(DIERef ref)> callback);
-
-  static void ExtractDIEArray(const DIEInfoArray &die_info_array,
-                              const dw_tag_t tag,
-                              const uint32_t qualified_name_hash,
-                              llvm::function_ref<bool(DIERef ref)> callback);
-
-  static void
-  ExtractClassOrStructDIEArray(const DIEInfoArray &die_info_array,
-                               bool return_implementation_only_if_available,
-                               llvm::function_ref<bool(DIERef ref)> callback);
-
-  static void
-  ExtractTypesFromDIEArray(const DIEInfoArray &die_info_array,
-                           uint32_t type_flag_mask, uint32_t type_flag_value,
-                           llvm::function_ref<bool(DIERef ref)> callback);
-
   static const char *GetAtomTypeName(uint16_t atom);
 };
 
index 301f773..ce5d2f6 100644 (file)
@@ -314,6 +314,13 @@ public:
   /// Return the Atom description, which can be used to interpret the raw values
   /// of the Accelerator Entries in this table.
   ArrayRef<std::pair<HeaderData::AtomType, HeaderData::Form>> getAtomsDesc();
+
+  /// Returns true iff `AtomTy` is one of the atoms available in Entries of this
+  /// table.
+  bool containsAtomType(HeaderData::AtomType AtomTy) const {
+    return is_contained(make_first_range(HdrData.Atoms), AtomTy);
+  }
+
   bool validateForms();
 
   /// Return information related to the DWARF DIE we're looking for when