| //=== 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" |
| |
| using namespace llvm; |
| using namespace dwarf_linker; |
| using namespace dwarf_linker::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, Entry.CU->getGlobalData().getOptions().Verbose); |
| |
| 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, Entry.CU->getGlobalData().getOptions().Verbose); |
| 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; |
| } |