Skip to content

Commit 213a44d

Browse files
committed
[dsymutil] Remove recursion from lookForChildDIEsToKeep (2/2) (NFC)
The functions lookForDIEsToKeep and keepDIEAndDependencies are mutually recursive and can cause a stackoverflow for large projects. While this has always been the case, it became a bigger issue when we parallelized dsymutil, because threads get only a fraction of the stack space. This patch removes the final recursive call from lookForDIEsToKeep. The call was used to look at the current DIE's parent chain and mark everything as kept. This was tested by running dsymutil on clang built in debug (both with and without modules) and comparing the MD5 hash of the generated dSYM companion file. Differential revision: https://reviews.llvm.org/D70994 (cherry picked from commit 9b15873)
1 parent bba74c1 commit 213a44d

File tree

1 file changed

+139
-74
lines changed

1 file changed

+139
-74
lines changed

llvm/tools/dsymutil/DwarfLinker.cpp

Lines changed: 139 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -759,9 +759,19 @@ unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr,
759759
namespace {
760760
/// The distinct types of work performed by the work loop.
761761
enum class WorklistItemType {
762+
/// Given a DIE, look for DIEs to be kept.
762763
LookForDIEsToKeep,
764+
/// Given a DIE, look for children of this DIE to be kept.
763765
LookForChildDIEsToKeep,
766+
/// Given a DIE, look for DIEs referencing this DIE to be kept.
767+
LookForRefDIEsToKeep,
768+
/// Given a DIE, look for parent DIEs to be kept.
769+
LookForParentDIEsToKeep,
770+
/// Given a DIE, update its incompleteness based on whether its children are
771+
/// incomplete.
764772
UpdateChildIncompleteness,
773+
/// Given a DIE, update its incompleteness based on whether the DIEs it
774+
/// references are incomplete.
765775
UpdateRefIncompleteness,
766776
};
767777

@@ -773,6 +783,7 @@ struct WorklistItem {
773783
DWARFDie Die;
774784
CompileUnit &CU;
775785
unsigned Flags;
786+
unsigned AncestorIdx = 0;
776787
CompileUnit::DIEInfo *OtherInfo = nullptr;
777788

778789
WorklistItem(DWARFDie Die, CompileUnit &CU, unsigned Flags,
@@ -782,6 +793,10 @@ struct WorklistItem {
782793
WorklistItem(DWARFDie Die, CompileUnit &CU, WorklistItemType T,
783794
CompileUnit::DIEInfo *OtherInfo = nullptr)
784795
: Type(T), Die(Die), CU(CU), OtherInfo(OtherInfo){};
796+
797+
WorklistItem(unsigned AncestorIdx, CompileUnit &CU, unsigned Flags)
798+
: Type(WorklistItemType::LookForParentDIEsToKeep), CU(CU), Flags(Flags),
799+
AncestorIdx(AncestorIdx){};
785800
};
786801
} // namespace
787802

@@ -806,8 +821,8 @@ static void updateChildIncompleteness(const DWARFDie &Die, CompileUnit &CU,
806821
}
807822

808823
/// Helper that updates the completeness of the current DIE based on the
809-
/// completeness of the DIEs referencing it. It depends on the incompleteness
810-
/// of the referencing DIE already being computed.
824+
/// completeness of the DIEs it references. It depends on the incompleteness of
825+
/// the referenced DIE already being computed.
811826
static void updateRefIncompleteness(const DWARFDie &Die, CompileUnit &CU,
812827
CompileUnit::DIEInfo &RefInfo) {
813828
switch (Die.getTag()) {
@@ -831,6 +846,8 @@ static void updateRefIncompleteness(const DWARFDie &Die, CompileUnit &CU,
831846
MyInfo.Incomplete = true;
832847
}
833848

849+
/// Look at the children of the given DIE and decide whether they should be
850+
/// kept.
834851
static void lookForChildDIEsToKeep(const DWARFDie &Die, CompileUnit &CU,
835852
unsigned Flags,
836853
SmallVectorImpl<WorklistItem> &Worklist) {
@@ -842,6 +859,8 @@ static void lookForChildDIEsToKeep(const DWARFDie &Die, CompileUnit &CU,
842859
if (dieNeedsChildrenToBeMeaningful(Die.getTag()))
843860
Flags &= ~DwarfLinker::TF_ParentWalk;
844861

862+
// We're finished if this DIE has no children or we're walking the parent
863+
// chain.
845864
if (!Die.hasChildren() || (Flags & DwarfLinker::TF_ParentWalk))
846865
return;
847866

@@ -858,6 +877,94 @@ static void lookForChildDIEsToKeep(const DWARFDie &Die, CompileUnit &CU,
858877
}
859878
}
860879

880+
/// Look at DIEs referenced by the given DIE and decide whether they should be
881+
/// kept. All DIEs referenced though attributes should be kept.
882+
static void lookForRefDIEsToKeep(const DWARFDie &Die, CompileUnit &CU,
883+
unsigned Flags, DwarfLinker &Linker,
884+
const UnitListTy &Units,
885+
const DebugMapObject &DMO,
886+
SmallVectorImpl<WorklistItem> &Worklist) {
887+
bool UseOdr = (Flags & DwarfLinker::TF_DependencyWalk)
888+
? (Flags & DwarfLinker::TF_ODR)
889+
: CU.hasODR();
890+
DWARFUnit &Unit = CU.getOrigUnit();
891+
DWARFDataExtractor Data = Unit.getDebugInfoExtractor();
892+
const auto *Abbrev = Die.getAbbreviationDeclarationPtr();
893+
uint64_t Offset = Die.getOffset() + getULEB128Size(Abbrev->getCode());
894+
895+
SmallVector<std::pair<DWARFDie, CompileUnit &>, 4> ReferencedDIEs;
896+
for (const auto &AttrSpec : Abbrev->attributes()) {
897+
DWARFFormValue Val(AttrSpec.Form);
898+
if (!Val.isFormClass(DWARFFormValue::FC_Reference) ||
899+
AttrSpec.Attr == dwarf::DW_AT_sibling) {
900+
DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset,
901+
Unit.getFormParams());
902+
continue;
903+
}
904+
905+
Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit);
906+
CompileUnit *ReferencedCU;
907+
if (auto RefDie =
908+
resolveDIEReference(Linker, DMO, Units, Val, Die, ReferencedCU)) {
909+
uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDie);
910+
CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx);
911+
bool IsModuleRef = Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() &&
912+
Info.Ctxt->isDefinedInClangModule();
913+
// If the referenced DIE has a DeclContext that has already been
914+
// emitted, then do not keep the one in this CU. We'll link to
915+
// the canonical DIE in cloneDieReferenceAttribute.
916+
//
917+
// FIXME: compatibility with dsymutil-classic. UseODR shouldn't
918+
// be necessary and could be advantageously replaced by
919+
// ReferencedCU->hasODR() && CU.hasODR().
920+
//
921+
// FIXME: compatibility with dsymutil-classic. There is no
922+
// reason not to unique ref_addr references.
923+
if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && (UseOdr || IsModuleRef) &&
924+
Info.Ctxt &&
925+
Info.Ctxt != ReferencedCU->getInfo(Info.ParentIdx).Ctxt &&
926+
Info.Ctxt->getCanonicalDIEOffset() && isODRAttribute(AttrSpec.Attr))
927+
continue;
928+
929+
// Keep a module forward declaration if there is no definition.
930+
if (!(isODRAttribute(AttrSpec.Attr) && Info.Ctxt &&
931+
Info.Ctxt->getCanonicalDIEOffset()))
932+
Info.Prune = false;
933+
ReferencedDIEs.emplace_back(RefDie, *ReferencedCU);
934+
}
935+
}
936+
937+
unsigned ODRFlag = UseOdr ? DwarfLinker::TF_ODR : 0;
938+
939+
// Add referenced DIEs in reverse order to the worklist to effectively
940+
// process them in order.
941+
for (auto &P : reverse(ReferencedDIEs)) {
942+
// Add a worklist item before every child to calculate incompleteness right
943+
// after the current child is processed.
944+
uint32_t RefIdx = P.second.getOrigUnit().getDIEIndex(P.first);
945+
CompileUnit::DIEInfo &Info = P.second.getInfo(RefIdx);
946+
Worklist.emplace_back(Die, CU, WorklistItemType::UpdateRefIncompleteness,
947+
&Info);
948+
Worklist.emplace_back(P.first, P.second,
949+
DwarfLinker::TF_Keep |
950+
DwarfLinker::TF_DependencyWalk | ODRFlag);
951+
}
952+
}
953+
954+
/// Look at the parent of the given DIE and decide whether they should be kept.
955+
static void lookForParentDIEsToKeep(unsigned AncestorIdx, CompileUnit &CU,
956+
unsigned Flags,
957+
SmallVectorImpl<WorklistItem> &Worklist) {
958+
// Stop if we encounter an ancestor that's already marked as kept.
959+
if (CU.getInfo(AncestorIdx).Keep)
960+
return;
961+
962+
DWARFUnit &Unit = CU.getOrigUnit();
963+
DWARFDie ParentDIE = Unit.getDIEAtIndex(AncestorIdx);
964+
Worklist.emplace_back(CU.getInfo(AncestorIdx).ParentIdx, CU, Flags);
965+
Worklist.emplace_back(ParentDIE, CU, Flags);
966+
}
967+
861968
/// Recursively walk the \p DIE tree and look for DIEs to keep. Store that
862969
/// information in \p CU's DIEInfo.
863970
///
@@ -871,6 +978,17 @@ static void lookForChildDIEsToKeep(const DWARFDie &Die, CompileUnit &CU,
871978
/// TF_DependencyWalk flag tells us which kind of traversal we are currently
872979
/// doing.
873980
///
981+
/// The recursive algorithm is implemented iteratively as a work list because
982+
/// very deep recursion could exhaust the stack for large projects. The work
983+
/// list acts as a scheduler for different types of work that need to be
984+
/// performed.
985+
///
986+
/// The recursive nature of the algorithm is simulated by running the "main"
987+
/// algorithm (LookForDIEsToKeep) followed by either looking at more DIEs
988+
/// (LookForChildDIEsToKeep, LookForRefDIEsToKeep, LookForParentDIEsToKeep) or
989+
/// fixing up a computed property (UpdateChildIncompleteness,
990+
/// UpdateRefIncompleteness).
991+
///
874992
/// The return value indicates whether the DIE is incomplete.
875993
void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
876994
RangesTy &Ranges, const UnitListTy &Units,
@@ -896,6 +1014,14 @@ void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
8961014
case WorklistItemType::LookForChildDIEsToKeep:
8971015
lookForChildDIEsToKeep(Current.Die, Current.CU, Current.Flags, Worklist);
8981016
continue;
1017+
case WorklistItemType::LookForRefDIEsToKeep:
1018+
lookForRefDIEsToKeep(Current.Die, Current.CU, Current.Flags, *this, Units,
1019+
DMO, Worklist);
1020+
continue;
1021+
case WorklistItemType::LookForParentDIEsToKeep:
1022+
lookForParentDIEsToKeep(Current.AncestorIdx, Current.CU, Current.Flags,
1023+
Worklist);
1024+
continue;
8991025
case WorklistItemType::LookForDIEsToKeep:
9001026
break;
9011027
}
@@ -929,12 +1055,6 @@ void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
9291055

9301056
// If it is a newly kept DIE mark it as well as all its dependencies as
9311057
// kept.
932-
bool UseOdr = (Current.Flags & TF_DependencyWalk)
933-
? (Current.Flags & TF_ODR)
934-
: Current.CU.hasODR();
935-
unsigned ODRFlag = UseOdr ? TF_ODR : 0;
936-
937-
DWARFUnit &Unit = Current.CU.getOrigUnit();
9381058
MyInfo.Keep = true;
9391059

9401060
// We're looking for incomplete types.
@@ -943,74 +1063,19 @@ void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
9431063
Current.Die.getTag() != dwarf::DW_TAG_member &&
9441064
dwarf::toUnsigned(Current.Die.find(dwarf::DW_AT_declaration), 0);
9451065

946-
// First mark all the parent chain as kept.
947-
unsigned AncestorIdx = MyInfo.ParentIdx;
948-
while (!Current.CU.getInfo(AncestorIdx).Keep) {
949-
lookForDIEsToKeep(
950-
RelocMgr, Ranges, Units, Unit.getDIEAtIndex(AncestorIdx), DMO,
951-
Current.CU, TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag);
952-
AncestorIdx = Current.CU.getInfo(AncestorIdx).ParentIdx;
953-
}
954-
955-
// Then we need to mark all the DIEs referenced by this DIE's attributes
956-
// as kept.
957-
DWARFDataExtractor Data = Unit.getDebugInfoExtractor();
958-
const auto *Abbrev = Current.Die.getAbbreviationDeclarationPtr();
959-
uint64_t Offset =
960-
Current.Die.getOffset() + getULEB128Size(Abbrev->getCode());
961-
962-
// Mark all DIEs referenced through attributes as kept.
963-
SmallVector<std::pair<DWARFDie, CompileUnit &>, 4> ReferencedDIEs;
964-
for (const auto &AttrSpec : Abbrev->attributes()) {
965-
DWARFFormValue Val(AttrSpec.Form);
966-
if (!Val.isFormClass(DWARFFormValue::FC_Reference) ||
967-
AttrSpec.Attr == dwarf::DW_AT_sibling) {
968-
DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset,
969-
Unit.getFormParams());
970-
continue;
971-
}
972-
973-
Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit);
974-
CompileUnit *ReferencedCU;
975-
if (auto RefDie = resolveDIEReference(*this, DMO, Units, Val,
976-
Current.Die, ReferencedCU)) {
977-
uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDie);
978-
CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx);
979-
bool IsModuleRef = Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() &&
980-
Info.Ctxt->isDefinedInClangModule();
981-
// If the referenced DIE has a DeclContext that has already been
982-
// emitted, then do not keep the one in this CU. We'll link to
983-
// the canonical DIE in cloneDieReferenceAttribute.
984-
// FIXME: compatibility with dsymutil-classic. UseODR shouldn't
985-
// be necessary and could be advantageously replaced by
986-
// ReferencedCU->hasODR() && CU.hasODR().
987-
// FIXME: compatibility with dsymutil-classic. There is no
988-
// reason not to unique ref_addr references.
989-
if (AttrSpec.Form != dwarf::DW_FORM_ref_addr &&
990-
(UseOdr || IsModuleRef) && Info.Ctxt &&
991-
Info.Ctxt != ReferencedCU->getInfo(Info.ParentIdx).Ctxt &&
992-
Info.Ctxt->getCanonicalDIEOffset() &&
993-
isODRAttribute(AttrSpec.Attr))
994-
continue;
1066+
// After looking at the parent chain, look for referenced DIEs. Because of
1067+
// the LIFO worklist we need to schedule that work before any subsequent
1068+
// items are added to the worklist.
1069+
Worklist.emplace_back(Current.Die, Current.CU, Current.Flags,
1070+
WorklistItemType::LookForRefDIEsToKeep);
9951071

996-
// Keep a module forward declaration if there is no definition.
997-
if (!(isODRAttribute(AttrSpec.Attr) && Info.Ctxt &&
998-
Info.Ctxt->getCanonicalDIEOffset()))
999-
Info.Prune = false;
1000-
ReferencedDIEs.emplace_back(RefDie, *ReferencedCU);
1001-
}
1002-
}
1072+
bool UseOdr = (Current.Flags & TF_DependencyWalk) ? (Current.Flags & TF_ODR)
1073+
: Current.CU.hasODR();
1074+
unsigned ODRFlag = UseOdr ? TF_ODR : 0;
1075+
unsigned ParFlags = TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag;
10031076

1004-
// Add referenced DIEs in reverse order to the worklist to effectively
1005-
// process them in order.
1006-
for (auto& P : reverse(ReferencedDIEs)) {
1007-
// Add a worklist item before every child to calculate incompleteness right
1008-
// after the current child is processed.
1009-
uint32_t RefIdx = P.second.getOrigUnit().getDIEIndex(P.first);
1010-
CompileUnit::DIEInfo &Info = P.second.getInfo(RefIdx);
1011-
Worklist.emplace_back(Current.Die, Current.CU, WorklistItemType::UpdateRefIncompleteness, &Info);
1012-
Worklist.emplace_back(P.first, P.second, TF_Keep | TF_DependencyWalk | ODRFlag);
1013-
}
1077+
// Now schedule the parent walk.
1078+
Worklist.emplace_back(MyInfo.ParentIdx, Current.CU, ParFlags);
10141079
}
10151080
}
10161081

0 commit comments

Comments
 (0)