837 lines
29 KiB
C++
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;
|
|
}
|