aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp839
1 files changed, 839 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp b/contrib/llvm-project/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp
new file mode 100644
index 000000000000..052eb6cf57d4
--- /dev/null
+++ b/contrib/llvm-project/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp
@@ -0,0 +1,839 @@
+//=== DependencyTracker.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 "DependencyTracker.h"
+#include "llvm/Support/FormatVariadic.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+/// A broken link in the keep chain. By recording both the parent and the child
+/// we can show only broken links for DIEs with multiple children.
+struct BrokenLink {
+ BrokenLink(DWARFDie Parent, DWARFDie Child, const char *Message)
+ : Parent(Parent), Child(Child), Message(Message) {}
+ DWARFDie Parent;
+ DWARFDie Child;
+ std::string Message;
+};
+
+/// Verify the keep chain by looking for DIEs that are kept but who's parent
+/// isn't.
+void DependencyTracker::verifyKeepChain() {
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ SmallVector<DWARFDie> Worklist;
+ Worklist.push_back(CU.getOrigUnit().getUnitDIE());
+
+ // List of broken links.
+ SmallVector<BrokenLink> BrokenLinks;
+
+ while (!Worklist.empty()) {
+ const DWARFDie Current = Worklist.back();
+ Worklist.pop_back();
+
+ if (!Current.isValid())
+ continue;
+
+ CompileUnit::DIEInfo &CurrentInfo =
+ CU.getDIEInfo(Current.getDebugInfoEntry());
+ const bool ParentPlainDieIsKept = CurrentInfo.needToKeepInPlainDwarf();
+ const bool ParentTypeDieIsKept = CurrentInfo.needToPlaceInTypeTable();
+
+ for (DWARFDie Child : reverse(Current.children())) {
+ Worklist.push_back(Child);
+
+ CompileUnit::DIEInfo &ChildInfo =
+ CU.getDIEInfo(Child.getDebugInfoEntry());
+ const bool ChildPlainDieIsKept = ChildInfo.needToKeepInPlainDwarf();
+ const bool ChildTypeDieIsKept = ChildInfo.needToPlaceInTypeTable();
+
+ if (!ParentPlainDieIsKept && ChildPlainDieIsKept)
+ BrokenLinks.emplace_back(Current, Child,
+ "Found invalid link in keep chain");
+
+ if (Child.getTag() == dwarf::DW_TAG_subprogram) {
+ if (!ChildInfo.getKeep() && isLiveSubprogramEntry(UnitEntryPairTy(
+ &CU, Child.getDebugInfoEntry()))) {
+ BrokenLinks.emplace_back(Current, Child,
+ "Live subprogram is not marked as kept");
+ }
+ }
+
+ if (!ChildInfo.getODRAvailable()) {
+ assert(!ChildTypeDieIsKept);
+ continue;
+ }
+
+ if (!ParentTypeDieIsKept && ChildTypeDieIsKept)
+ BrokenLinks.emplace_back(Current, Child,
+ "Found invalid link in keep chain");
+
+ if (CurrentInfo.getIsInAnonNamespaceScope() &&
+ ChildInfo.needToPlaceInTypeTable()) {
+ BrokenLinks.emplace_back(Current, Child,
+ "Found invalid placement marking for member "
+ "of anonymous namespace");
+ }
+ }
+ }
+
+ if (!BrokenLinks.empty()) {
+ for (BrokenLink Link : BrokenLinks) {
+ errs() << "\n=================================\n";
+ WithColor::error() << formatv("{0} between {1:x} and {2:x}", Link.Message,
+ Link.Parent.getOffset(),
+ Link.Child.getOffset());
+
+ errs() << "\nParent:";
+ Link.Parent.dump(errs(), 0, {});
+ errs() << "\n";
+ CU.getDIEInfo(Link.Parent).dump();
+
+ errs() << "\nChild:";
+ Link.Child.dump(errs(), 2, {});
+ errs() << "\n";
+ CU.getDIEInfo(Link.Child).dump();
+ }
+ report_fatal_error("invalid keep chain");
+ }
+#endif
+}
+
+bool DependencyTracker::resolveDependenciesAndMarkLiveness(
+ bool InterCUProcessingStarted, std::atomic<bool> &HasNewInterconnectedCUs) {
+ RootEntriesWorkList.clear();
+
+ // Search for live root DIEs.
+ CompileUnit::DIEInfo &CUInfo = CU.getDIEInfo(CU.getDebugInfoEntry(0));
+ CUInfo.setPlacement(CompileUnit::PlainDwarf);
+ collectRootsToKeep(UnitEntryPairTy{&CU, CU.getDebugInfoEntry(0)},
+ std::nullopt, false);
+
+ // Mark live DIEs as kept.
+ return markCollectedLiveRootsAsKept(InterCUProcessingStarted,
+ HasNewInterconnectedCUs);
+}
+
+void DependencyTracker::addActionToRootEntriesWorkList(
+ LiveRootWorklistActionTy Action, const UnitEntryPairTy &Entry,
+ std::optional<UnitEntryPairTy> ReferencedBy) {
+ if (ReferencedBy) {
+ RootEntriesWorkList.emplace_back(Action, Entry, *ReferencedBy);
+ return;
+ }
+
+ RootEntriesWorkList.emplace_back(Action, Entry);
+}
+
+void DependencyTracker::collectRootsToKeep(
+ const UnitEntryPairTy &Entry, std::optional<UnitEntryPairTy> ReferencedBy,
+ bool IsLiveParent) {
+ for (const DWARFDebugInfoEntry *CurChild =
+ Entry.CU->getFirstChildEntry(Entry.DieEntry);
+ CurChild && CurChild->getAbbreviationDeclarationPtr();
+ CurChild = Entry.CU->getSiblingEntry(CurChild)) {
+ UnitEntryPairTy ChildEntry(Entry.CU, CurChild);
+ CompileUnit::DIEInfo &ChildInfo = Entry.CU->getDIEInfo(CurChild);
+
+ bool IsLiveChild = false;
+
+ switch (CurChild->getTag()) {
+ case dwarf::DW_TAG_label: {
+ IsLiveChild = isLiveSubprogramEntry(ChildEntry);
+
+ // Keep label referencing live address.
+ // Keep label which is child of live parent entry.
+ if (IsLiveChild || (IsLiveParent && ChildInfo.getHasAnAddress())) {
+ addActionToRootEntriesWorkList(
+ LiveRootWorklistActionTy::MarkLiveEntryRec, ChildEntry,
+ ReferencedBy);
+ }
+ } break;
+ case dwarf::DW_TAG_subprogram: {
+ IsLiveChild = isLiveSubprogramEntry(ChildEntry);
+
+ // Keep subprogram referencing live address.
+ if (IsLiveChild) {
+ // If subprogram is in module scope and this module allows ODR
+ // deduplication set "TypeTable" placement, otherwise set "" placement
+ LiveRootWorklistActionTy Action =
+ (ChildInfo.getIsInMouduleScope() && ChildInfo.getODRAvailable())
+ ? LiveRootWorklistActionTy::MarkTypeEntryRec
+ : LiveRootWorklistActionTy::MarkLiveEntryRec;
+
+ addActionToRootEntriesWorkList(Action, ChildEntry, ReferencedBy);
+ }
+ } break;
+ case dwarf::DW_TAG_constant:
+ case dwarf::DW_TAG_variable: {
+ IsLiveChild = isLiveVariableEntry(ChildEntry, IsLiveParent);
+
+ // Keep variable referencing live address.
+ if (IsLiveChild) {
+ // If variable is in module scope and this module allows ODR
+ // deduplication set "TypeTable" placement, otherwise set "" placement
+
+ LiveRootWorklistActionTy Action =
+ (ChildInfo.getIsInMouduleScope() && ChildInfo.getODRAvailable())
+ ? LiveRootWorklistActionTy::MarkTypeEntryRec
+ : LiveRootWorklistActionTy::MarkLiveEntryRec;
+
+ addActionToRootEntriesWorkList(Action, ChildEntry, ReferencedBy);
+ }
+ } break;
+ case dwarf::DW_TAG_base_type: {
+ // Always keep base types.
+ addActionToRootEntriesWorkList(
+ LiveRootWorklistActionTy::MarkSingleLiveEntry, ChildEntry,
+ ReferencedBy);
+ } break;
+ case dwarf::DW_TAG_imported_module:
+ case dwarf::DW_TAG_imported_declaration:
+ case dwarf::DW_TAG_imported_unit: {
+ // Always keep DIEs having DW_AT_import attribute.
+ if (Entry.DieEntry->getTag() == dwarf::DW_TAG_compile_unit) {
+ addActionToRootEntriesWorkList(
+ LiveRootWorklistActionTy::MarkSingleLiveEntry, ChildEntry,
+ ReferencedBy);
+ break;
+ }
+
+ addActionToRootEntriesWorkList(
+ LiveRootWorklistActionTy::MarkSingleTypeEntry, ChildEntry,
+ ReferencedBy);
+ } break;
+ case dwarf::DW_TAG_type_unit:
+ case dwarf::DW_TAG_partial_unit:
+ case dwarf::DW_TAG_compile_unit: {
+ llvm_unreachable("Called for incorrect DIE");
+ } break;
+ default:
+ // Nothing to do.
+ break;
+ }
+
+ collectRootsToKeep(ChildEntry, ReferencedBy, IsLiveChild || IsLiveParent);
+ }
+}
+
+bool DependencyTracker::markCollectedLiveRootsAsKept(
+ bool InterCUProcessingStarted, std::atomic<bool> &HasNewInterconnectedCUs) {
+ bool Res = true;
+
+ // Mark roots as kept.
+ while (!RootEntriesWorkList.empty()) {
+ LiveRootWorklistItemTy Root = RootEntriesWorkList.pop_back_val();
+
+ if (markDIEEntryAsKeptRec(Root.getAction(), Root.getRootEntry(),
+ Root.getRootEntry(), InterCUProcessingStarted,
+ HasNewInterconnectedCUs)) {
+ if (Root.hasReferencedByOtherEntry())
+ Dependencies.push_back(Root);
+ } else
+ Res = false;
+ }
+
+ return Res;
+}
+
+bool DependencyTracker::updateDependenciesCompleteness() {
+ bool HasNewDependency = false;
+ for (LiveRootWorklistItemTy &Root : Dependencies) {
+ assert(Root.hasReferencedByOtherEntry() &&
+ "Root entry without dependency inside the dependencies list");
+
+ UnitEntryPairTy RootEntry = Root.getRootEntry();
+ CompileUnit::DIEInfo &RootInfo =
+ RootEntry.CU->getDIEInfo(RootEntry.DieEntry);
+
+ UnitEntryPairTy ReferencedByEntry = Root.getReferencedByEntry();
+ CompileUnit::DIEInfo &ReferencedByInfo =
+ ReferencedByEntry.CU->getDIEInfo(ReferencedByEntry.DieEntry);
+
+ if (!RootInfo.needToPlaceInTypeTable() &&
+ ReferencedByInfo.needToPlaceInTypeTable()) {
+ HasNewDependency = true;
+ setPlainDwarfPlacementRec(ReferencedByEntry);
+
+ // FIXME: we probably need to update getKeepTypeChildren status for
+ // parents of *Root.ReferencedBy.
+ }
+ }
+
+ return HasNewDependency;
+}
+
+void DependencyTracker::setPlainDwarfPlacementRec(
+ const UnitEntryPairTy &Entry) {
+ CompileUnit::DIEInfo &Info = Entry.CU->getDIEInfo(Entry.DieEntry);
+ if (Info.getPlacement() == CompileUnit::PlainDwarf &&
+ !Info.getKeepTypeChildren())
+ return;
+
+ Info.setPlacement(CompileUnit::PlainDwarf);
+ Info.unsetKeepTypeChildren();
+ markParentsAsKeepingChildren(Entry);
+
+ for (const DWARFDebugInfoEntry *CurChild =
+ Entry.CU->getFirstChildEntry(Entry.DieEntry);
+ CurChild && CurChild->getAbbreviationDeclarationPtr();
+ CurChild = Entry.CU->getSiblingEntry(CurChild))
+ setPlainDwarfPlacementRec(UnitEntryPairTy{Entry.CU, CurChild});
+}
+
+static bool isNamespaceLikeEntry(const DWARFDebugInfoEntry *Entry) {
+ switch (Entry->getTag()) {
+ case dwarf::DW_TAG_compile_unit:
+ case dwarf::DW_TAG_module:
+ case dwarf::DW_TAG_namespace:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool isAlreadyMarked(const CompileUnit::DIEInfo &Info,
+ CompileUnit::DieOutputPlacement NewPlacement) {
+ if (!Info.getKeep())
+ return false;
+
+ switch (NewPlacement) {
+ case CompileUnit::TypeTable:
+ return Info.needToPlaceInTypeTable();
+
+ case CompileUnit::PlainDwarf:
+ return Info.needToKeepInPlainDwarf();
+
+ case CompileUnit::Both:
+ return Info.needToPlaceInTypeTable() && Info.needToKeepInPlainDwarf();
+
+ case CompileUnit::NotSet:
+ llvm_unreachable("Unset placement type is specified.");
+ };
+
+ llvm_unreachable("Unknown CompileUnit::DieOutputPlacement enum");
+}
+
+bool isAlreadyMarked(const UnitEntryPairTy &Entry,
+ CompileUnit::DieOutputPlacement NewPlacement) {
+ return isAlreadyMarked(Entry.CU->getDIEInfo(Entry.DieEntry), NewPlacement);
+}
+
+void DependencyTracker::markParentsAsKeepingChildren(
+ const UnitEntryPairTy &Entry) {
+ if (Entry.DieEntry->getAbbreviationDeclarationPtr() == nullptr)
+ return;
+
+ CompileUnit::DIEInfo &Info = Entry.CU->getDIEInfo(Entry.DieEntry);
+ bool NeedKeepTypeChildren = Info.needToPlaceInTypeTable();
+ bool NeedKeepPlainChildren = Info.needToKeepInPlainDwarf();
+
+ bool AreTypeParentsDone = !NeedKeepTypeChildren;
+ bool ArePlainParentsDone = !NeedKeepPlainChildren;
+
+ // Mark parents as 'Keep*Children'.
+ std::optional<uint32_t> ParentIdx = Entry.DieEntry->getParentIdx();
+ while (ParentIdx) {
+ const DWARFDebugInfoEntry *ParentEntry =
+ Entry.CU->getDebugInfoEntry(*ParentIdx);
+ CompileUnit::DIEInfo &ParentInfo = Entry.CU->getDIEInfo(*ParentIdx);
+
+ if (!AreTypeParentsDone && NeedKeepTypeChildren) {
+ if (ParentInfo.getKeepTypeChildren())
+ AreTypeParentsDone = true;
+ else {
+ bool AddToWorklist = !isAlreadyMarked(
+ ParentInfo, CompileUnit::DieOutputPlacement::TypeTable);
+ ParentInfo.setKeepTypeChildren();
+ if (AddToWorklist && !isNamespaceLikeEntry(ParentEntry)) {
+ addActionToRootEntriesWorkList(
+ LiveRootWorklistActionTy::MarkTypeChildrenRec,
+ UnitEntryPairTy{Entry.CU, ParentEntry}, std::nullopt);
+ }
+ }
+ }
+
+ if (!ArePlainParentsDone && NeedKeepPlainChildren) {
+ if (ParentInfo.getKeepPlainChildren())
+ ArePlainParentsDone = true;
+ else {
+ bool AddToWorklist = !isAlreadyMarked(
+ ParentInfo, CompileUnit::DieOutputPlacement::PlainDwarf);
+ ParentInfo.setKeepPlainChildren();
+ if (AddToWorklist && !isNamespaceLikeEntry(ParentEntry)) {
+ addActionToRootEntriesWorkList(
+ LiveRootWorklistActionTy::MarkLiveChildrenRec,
+ UnitEntryPairTy{Entry.CU, ParentEntry}, std::nullopt);
+ }
+ }
+ }
+
+ if (AreTypeParentsDone && ArePlainParentsDone)
+ break;
+
+ ParentIdx = ParentEntry->getParentIdx();
+ }
+}
+
+// This function tries to set specified \p Placement for the \p Entry.
+// Depending on the concrete entry, the placement could be:
+// a) changed to another.
+// b) joined with current entry placement.
+// c) set as requested.
+static CompileUnit::DieOutputPlacement
+getFinalPlacementForEntry(const UnitEntryPairTy &Entry,
+ CompileUnit::DieOutputPlacement Placement) {
+ assert((Placement != CompileUnit::NotSet) && "Placement is not set");
+ CompileUnit::DIEInfo &EntryInfo = Entry.CU->getDIEInfo(Entry.DieEntry);
+
+ if (!EntryInfo.getODRAvailable())
+ return CompileUnit::PlainDwarf;
+
+ if (Entry.DieEntry->getTag() == dwarf::DW_TAG_variable) {
+ // Do not put variable into the "TypeTable" and "PlainDwarf" at the same
+ // time.
+ if (EntryInfo.getPlacement() == CompileUnit::PlainDwarf ||
+ EntryInfo.getPlacement() == CompileUnit::Both)
+ return CompileUnit::PlainDwarf;
+
+ if (Placement == CompileUnit::PlainDwarf || Placement == CompileUnit::Both)
+ return CompileUnit::PlainDwarf;
+ }
+
+ switch (EntryInfo.getPlacement()) {
+ case CompileUnit::NotSet:
+ return Placement;
+
+ case CompileUnit::TypeTable:
+ return Placement == CompileUnit::PlainDwarf ? CompileUnit::Both : Placement;
+
+ case CompileUnit::PlainDwarf:
+ return Placement == CompileUnit::TypeTable ? CompileUnit::Both : Placement;
+
+ case CompileUnit::Both:
+ return CompileUnit::Both;
+ };
+
+ llvm_unreachable("Unknown placement type.");
+ return Placement;
+}
+
+bool DependencyTracker::markDIEEntryAsKeptRec(
+ LiveRootWorklistActionTy Action, const UnitEntryPairTy &RootEntry,
+ const UnitEntryPairTy &Entry, bool InterCUProcessingStarted,
+ std::atomic<bool> &HasNewInterconnectedCUs) {
+ if (Entry.DieEntry->getAbbreviationDeclarationPtr() == nullptr)
+ return true;
+
+ CompileUnit::DIEInfo &Info = Entry.CU->getDIEInfo(Entry.DieEntry);
+
+ // Calculate final placement placement.
+ CompileUnit::DieOutputPlacement Placement = getFinalPlacementForEntry(
+ Entry,
+ isLiveAction(Action) ? CompileUnit::PlainDwarf : CompileUnit::TypeTable);
+ assert((Info.getODRAvailable() || isLiveAction(Action) ||
+ Placement == CompileUnit::PlainDwarf) &&
+ "Wrong kind of placement for ODR unavailable entry");
+
+ if (!isChildrenAction(Action))
+ if (isAlreadyMarked(Entry, Placement))
+ return true;
+
+ // Mark current DIE as kept.
+ Info.setKeep();
+ Info.setPlacement(Placement);
+
+ // Set keep children property for parents.
+ markParentsAsKeepingChildren(Entry);
+
+ UnitEntryPairTy FinalRootEntry =
+ Entry.DieEntry->getTag() == dwarf::DW_TAG_subprogram ? Entry : RootEntry;
+
+ // Analyse referenced DIEs.
+ bool Res = true;
+ if (!maybeAddReferencedRoots(Action, FinalRootEntry, Entry,
+ InterCUProcessingStarted,
+ HasNewInterconnectedCUs))
+ Res = false;
+
+ // Return if we do not need to process children.
+ if (isSingleAction(Action))
+ return Res;
+
+ // Process children.
+ // Check for subprograms special case.
+ if (Entry.DieEntry->getTag() == dwarf::DW_TAG_subprogram &&
+ Info.getODRAvailable()) {
+ // Subprograms is a special case. As it can be root for type DIEs
+ // and itself may be subject to move into the artificial type unit.
+ // a) Non removable children(like DW_TAG_formal_parameter) should always
+ // be cloned. They are placed into the "PlainDwarf" and into the
+ // "TypeTable".
+ // b) ODR deduplication candidates(type DIEs) children should not be put
+ // into the "PlainDwarf".
+ // c) Children keeping addresses and locations(like DW_TAG_call_site)
+ // should not be put into the "TypeTable".
+ for (const DWARFDebugInfoEntry *CurChild =
+ Entry.CU->getFirstChildEntry(Entry.DieEntry);
+ CurChild && CurChild->getAbbreviationDeclarationPtr();
+ CurChild = Entry.CU->getSiblingEntry(CurChild)) {
+ CompileUnit::DIEInfo ChildInfo = Entry.CU->getDIEInfo(CurChild);
+
+ switch (CurChild->getTag()) {
+ case dwarf::DW_TAG_variable:
+ case dwarf::DW_TAG_constant:
+ case dwarf::DW_TAG_subprogram:
+ case dwarf::DW_TAG_label: {
+ if (ChildInfo.getHasAnAddress())
+ continue;
+ } break;
+
+ // Entries having following tags could not be removed from the subprogram.
+ case dwarf::DW_TAG_lexical_block:
+ case dwarf::DW_TAG_friend:
+ case dwarf::DW_TAG_inheritance:
+ case dwarf::DW_TAG_formal_parameter:
+ case dwarf::DW_TAG_unspecified_parameters:
+ case dwarf::DW_TAG_template_type_parameter:
+ case dwarf::DW_TAG_template_value_parameter:
+ case dwarf::DW_TAG_GNU_template_parameter_pack:
+ case dwarf::DW_TAG_GNU_formal_parameter_pack:
+ case dwarf::DW_TAG_GNU_template_template_param:
+ case dwarf::DW_TAG_thrown_type: {
+ // Go to the default child handling.
+ } break;
+
+ default: {
+ bool ChildIsTypeTableCandidate = isTypeTableCandidate(CurChild);
+
+ // Skip child marked to be copied into the artificial type unit.
+ if (isLiveAction(Action) && ChildIsTypeTableCandidate)
+ continue;
+
+ // Skip child marked to be copied into the plain unit.
+ if (isTypeAction(Action) && !ChildIsTypeTableCandidate)
+ continue;
+
+ // Go to the default child handling.
+ } break;
+ }
+
+ if (!markDIEEntryAsKeptRec(
+ Action, FinalRootEntry, UnitEntryPairTy{Entry.CU, CurChild},
+ InterCUProcessingStarted, HasNewInterconnectedCUs))
+ Res = false;
+ }
+
+ return Res;
+ }
+
+ // Recursively process children.
+ for (const DWARFDebugInfoEntry *CurChild =
+ Entry.CU->getFirstChildEntry(Entry.DieEntry);
+ CurChild && CurChild->getAbbreviationDeclarationPtr();
+ CurChild = Entry.CU->getSiblingEntry(CurChild)) {
+ CompileUnit::DIEInfo ChildInfo = Entry.CU->getDIEInfo(CurChild);
+ switch (CurChild->getTag()) {
+ case dwarf::DW_TAG_variable:
+ case dwarf::DW_TAG_constant:
+ case dwarf::DW_TAG_subprogram:
+ case dwarf::DW_TAG_label: {
+ if (ChildInfo.getHasAnAddress())
+ continue;
+ } break;
+ default:
+ break; // Nothing to do.
+ };
+
+ if (!markDIEEntryAsKeptRec(
+ Action, FinalRootEntry, UnitEntryPairTy{Entry.CU, CurChild},
+ InterCUProcessingStarted, HasNewInterconnectedCUs))
+ Res = false;
+ }
+
+ return Res;
+}
+
+bool DependencyTracker::isTypeTableCandidate(
+ const DWARFDebugInfoEntry *DIEEntry) {
+ switch (DIEEntry->getTag()) {
+ default:
+ return false;
+
+ case dwarf::DW_TAG_imported_module:
+ case dwarf::DW_TAG_imported_declaration:
+ case dwarf::DW_TAG_imported_unit:
+ case dwarf::DW_TAG_array_type:
+ case dwarf::DW_TAG_class_type:
+ case dwarf::DW_TAG_enumeration_type:
+ case dwarf::DW_TAG_pointer_type:
+ case dwarf::DW_TAG_reference_type:
+ case dwarf::DW_TAG_string_type:
+ case dwarf::DW_TAG_structure_type:
+ case dwarf::DW_TAG_subroutine_type:
+ case dwarf::DW_TAG_typedef:
+ case dwarf::DW_TAG_union_type:
+ case dwarf::DW_TAG_variant:
+ case dwarf::DW_TAG_module:
+ case dwarf::DW_TAG_ptr_to_member_type:
+ case dwarf::DW_TAG_set_type:
+ case dwarf::DW_TAG_subrange_type:
+ case dwarf::DW_TAG_base_type:
+ case dwarf::DW_TAG_const_type:
+ case dwarf::DW_TAG_enumerator:
+ case dwarf::DW_TAG_file_type:
+ case dwarf::DW_TAG_packed_type:
+ case dwarf::DW_TAG_thrown_type:
+ case dwarf::DW_TAG_volatile_type:
+ case dwarf::DW_TAG_dwarf_procedure:
+ case dwarf::DW_TAG_restrict_type:
+ case dwarf::DW_TAG_interface_type:
+ case dwarf::DW_TAG_namespace:
+ case dwarf::DW_TAG_unspecified_type:
+ case dwarf::DW_TAG_shared_type:
+ case dwarf::DW_TAG_rvalue_reference_type:
+ case dwarf::DW_TAG_coarray_type:
+ case dwarf::DW_TAG_dynamic_type:
+ case dwarf::DW_TAG_atomic_type:
+ case dwarf::DW_TAG_immutable_type:
+ case dwarf::DW_TAG_function_template:
+ case dwarf::DW_TAG_class_template:
+ return true;
+ }
+}
+
+bool DependencyTracker::maybeAddReferencedRoots(
+ LiveRootWorklistActionTy Action, const UnitEntryPairTy &RootEntry,
+ const UnitEntryPairTy &Entry, bool InterCUProcessingStarted,
+ std::atomic<bool> &HasNewInterconnectedCUs) {
+ const auto *Abbrev = Entry.DieEntry->getAbbreviationDeclarationPtr();
+ if (Abbrev == nullptr)
+ return true;
+
+ DWARFUnit &Unit = Entry.CU->getOrigUnit();
+ DWARFDataExtractor Data = Unit.getDebugInfoExtractor();
+ uint64_t Offset =
+ Entry.DieEntry->getOffset() + getULEB128Size(Abbrev->getCode());
+
+ // For each DIE attribute...
+ for (const auto &AttrSpec : Abbrev->attributes()) {
+ DWARFFormValue Val(AttrSpec.Form);
+ if (!Val.isFormClass(DWARFFormValue::FC_Reference) ||
+ AttrSpec.Attr == dwarf::DW_AT_sibling) {
+ DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset,
+ Unit.getFormParams());
+ continue;
+ }
+ Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit);
+
+ // Resolve reference.
+ std::optional<UnitEntryPairTy> RefDie = Entry.CU->resolveDIEReference(
+ Val, InterCUProcessingStarted
+ ? ResolveInterCUReferencesMode::Resolve
+ : ResolveInterCUReferencesMode::AvoidResolving);
+ if (!RefDie) {
+ Entry.CU->warn("cann't find referenced DIE", Entry.DieEntry);
+ continue;
+ }
+
+ if (!RefDie->DieEntry) {
+ // Delay resolving reference.
+ RefDie->CU->setInterconnectedCU();
+ Entry.CU->setInterconnectedCU();
+ HasNewInterconnectedCUs = true;
+ return false;
+ }
+
+ assert((Entry.CU->getUniqueID() == RefDie->CU->getUniqueID() ||
+ InterCUProcessingStarted) &&
+ "Inter-CU reference while inter-CU processing is not started");
+
+ CompileUnit::DIEInfo &RefInfo = RefDie->CU->getDIEInfo(RefDie->DieEntry);
+ if (!RefInfo.getODRAvailable())
+ Action = LiveRootWorklistActionTy::MarkLiveEntryRec;
+ else if (RefInfo.getODRAvailable() &&
+ llvm::is_contained(getODRAttributes(), AttrSpec.Attr))
+ // Note: getODRAttributes does not include DW_AT_containing_type.
+ // It should be OK as we do getRootForSpecifiedEntry(). So any containing
+ // type would be found as the root for the entry.
+ Action = LiveRootWorklistActionTy::MarkTypeEntryRec;
+ else if (isLiveAction(Action))
+ Action = LiveRootWorklistActionTy::MarkLiveEntryRec;
+ else
+ Action = LiveRootWorklistActionTy::MarkTypeEntryRec;
+
+ if (AttrSpec.Attr == dwarf::DW_AT_import) {
+ if (isNamespaceLikeEntry(RefDie->DieEntry)) {
+ addActionToRootEntriesWorkList(
+ isTypeAction(Action)
+ ? LiveRootWorklistActionTy::MarkSingleTypeEntry
+ : LiveRootWorklistActionTy::MarkSingleLiveEntry,
+ *RefDie, RootEntry);
+ continue;
+ }
+
+ addActionToRootEntriesWorkList(Action, *RefDie, RootEntry);
+ continue;
+ }
+
+ UnitEntryPairTy RootForReferencedDie = getRootForSpecifiedEntry(*RefDie);
+ addActionToRootEntriesWorkList(Action, RootForReferencedDie, RootEntry);
+ }
+
+ return true;
+}
+
+UnitEntryPairTy
+DependencyTracker::getRootForSpecifiedEntry(UnitEntryPairTy Entry) {
+ UnitEntryPairTy Result = Entry;
+
+ do {
+ switch (Entry.DieEntry->getTag()) {
+ case dwarf::DW_TAG_subprogram:
+ case dwarf::DW_TAG_label:
+ case dwarf::DW_TAG_variable:
+ case dwarf::DW_TAG_constant: {
+ return Result;
+ } break;
+
+ default: {
+ // Nothing to do.
+ }
+ }
+
+ std::optional<uint32_t> ParentIdx = Result.DieEntry->getParentIdx();
+ if (!ParentIdx)
+ return Result;
+
+ const DWARFDebugInfoEntry *ParentEntry =
+ Result.CU->getDebugInfoEntry(*ParentIdx);
+ if (isNamespaceLikeEntry(ParentEntry))
+ break;
+ Result.DieEntry = ParentEntry;
+ } while (true);
+
+ return Result;
+}
+
+bool DependencyTracker::isLiveVariableEntry(const UnitEntryPairTy &Entry,
+ bool IsLiveParent) {
+ DWARFDie DIE = Entry.CU->getDIE(Entry.DieEntry);
+ CompileUnit::DIEInfo &Info = Entry.CU->getDIEInfo(DIE);
+
+ if (Info.getTrackLiveness()) {
+ const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
+
+ if (!Info.getIsInFunctionScope() &&
+ Abbrev->findAttributeIndex(dwarf::DW_AT_const_value)) {
+ // Global variables with constant value can always be kept.
+ } else {
+ // See if there is a relocation to a valid debug map entry inside this
+ // variable's location. The order is important here. We want to always
+ // check if the variable has a location expression address. However, we
+ // don't want a static variable in a function to force us to keep the
+ // enclosing function, unless requested explicitly.
+ std::pair<bool, std::optional<int64_t>> LocExprAddrAndRelocAdjustment =
+ Entry.CU->getContaingFile().Addresses->getVariableRelocAdjustment(
+ DIE);
+
+ if (LocExprAddrAndRelocAdjustment.first)
+ Info.setHasAnAddress();
+
+ if (!LocExprAddrAndRelocAdjustment.second)
+ return false;
+
+ if (!IsLiveParent && Info.getIsInFunctionScope() &&
+ !Entry.CU->getGlobalData().getOptions().KeepFunctionForStatic)
+ return false;
+ }
+ }
+ Info.setHasAnAddress();
+
+ if (Entry.CU->getGlobalData().getOptions().Verbose) {
+ outs() << "Keeping variable DIE:";
+ DIDumpOptions DumpOpts;
+ DumpOpts.ChildRecurseDepth = 0;
+ DumpOpts.Verbose = Entry.CU->getGlobalData().getOptions().Verbose;
+ DIE.dump(outs(), 8 /* Indent */, DumpOpts);
+ }
+
+ return true;
+}
+
+bool DependencyTracker::isLiveSubprogramEntry(const UnitEntryPairTy &Entry) {
+ DWARFDie DIE = Entry.CU->getDIE(Entry.DieEntry);
+ CompileUnit::DIEInfo &Info = Entry.CU->getDIEInfo(Entry.DieEntry);
+ std::optional<DWARFFormValue> LowPCVal = DIE.find(dwarf::DW_AT_low_pc);
+
+ std::optional<uint64_t> LowPc;
+ std::optional<uint64_t> HighPc;
+ std::optional<int64_t> RelocAdjustment;
+ if (Info.getTrackLiveness()) {
+ LowPc = dwarf::toAddress(LowPCVal);
+ if (!LowPc)
+ return false;
+
+ Info.setHasAnAddress();
+
+ RelocAdjustment =
+ Entry.CU->getContaingFile().Addresses->getSubprogramRelocAdjustment(
+ DIE);
+ if (!RelocAdjustment)
+ return false;
+
+ if (DIE.getTag() == dwarf::DW_TAG_subprogram) {
+ // Validate subprogram address range.
+
+ HighPc = DIE.getHighPC(*LowPc);
+ if (!HighPc) {
+ Entry.CU->warn("function without high_pc. Range will be discarded.",
+ &DIE);
+ return false;
+ }
+
+ if (*LowPc > *HighPc) {
+ Entry.CU->warn("low_pc greater than high_pc. Range will be discarded.",
+ &DIE);
+ return false;
+ }
+ } else if (DIE.getTag() == dwarf::DW_TAG_label) {
+ if (Entry.CU->hasLabelAt(*LowPc))
+ return false;
+
+ // FIXME: dsymutil-classic compat. dsymutil-classic doesn't consider
+ // labels that don't fall into the CU's aranges. This is wrong IMO. Debug
+ // info generation bugs aside, this is really wrong in the case of labels,
+ // where a label marking the end of a function will have a PC == CU's
+ // high_pc.
+ if (dwarf::toAddress(Entry.CU->find(Entry.DieEntry, dwarf::DW_AT_high_pc))
+ .value_or(UINT64_MAX) <= LowPc)
+ return false;
+
+ Entry.CU->addLabelLowPc(*LowPc, *RelocAdjustment);
+ }
+ } else
+ Info.setHasAnAddress();
+
+ if (Entry.CU->getGlobalData().getOptions().Verbose) {
+ outs() << "Keeping subprogram DIE:";
+ DIDumpOptions DumpOpts;
+ DumpOpts.ChildRecurseDepth = 0;
+ DumpOpts.Verbose = Entry.CU->getGlobalData().getOptions().Verbose;
+ DIE.dump(outs(), 8 /* Indent */, DumpOpts);
+ }
+
+ if (!Info.getTrackLiveness() || DIE.getTag() == dwarf::DW_TAG_label)
+ return true;
+
+ Entry.CU->addFunctionRange(*LowPc, *HighPc, *RelocAdjustment);
+ return true;
+}
+
+} // end of namespace dwarflinker_parallel
+} // namespace llvm