bolt/deps/llvm-18.1.8/llvm/lib/DWARFLinker/Parallel/DependencyTracker.cpp
2025-02-14 19:21:04 +01:00

837 lines
29 KiB
C++

//=== 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;
}