Skip to content

Commit 9b15873

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
1 parent 95a8e8a commit 9b15873

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
@@ -763,9 +763,19 @@ unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr,
763763
namespace {
764764
/// The distinct types of work performed by the work loop.
765765
enum class WorklistItemType {
766+
/// Given a DIE, look for DIEs to be kept.
766767
LookForDIEsToKeep,
768+
/// Given a DIE, look for children of this DIE to be kept.
767769
LookForChildDIEsToKeep,
770+
/// Given a DIE, look for DIEs referencing this DIE to be kept.
771+
LookForRefDIEsToKeep,
772+
/// Given a DIE, look for parent DIEs to be kept.
773+
LookForParentDIEsToKeep,
774+
/// Given a DIE, update its incompleteness based on whether its children are
775+
/// incomplete.
768776
UpdateChildIncompleteness,
777+
/// Given a DIE, update its incompleteness based on whether the DIEs it
778+
/// references are incomplete.
769779
UpdateRefIncompleteness,
770780
};
771781

@@ -777,6 +787,7 @@ struct WorklistItem {
777787
DWARFDie Die;
778788
CompileUnit &CU;
779789
unsigned Flags;
790+
unsigned AncestorIdx = 0;
780791
CompileUnit::DIEInfo *OtherInfo = nullptr;
781792

782793
WorklistItem(DWARFDie Die, CompileUnit &CU, unsigned Flags,
@@ -786,6 +797,10 @@ struct WorklistItem {
786797
WorklistItem(DWARFDie Die, CompileUnit &CU, WorklistItemType T,
787798
CompileUnit::DIEInfo *OtherInfo = nullptr)
788799
: Type(T), Die(Die), CU(CU), OtherInfo(OtherInfo){};
800+
801+
WorklistItem(unsigned AncestorIdx, CompileUnit &CU, unsigned Flags)
802+
: Type(WorklistItemType::LookForParentDIEsToKeep), CU(CU), Flags(Flags),
803+
AncestorIdx(AncestorIdx){};
789804
};
790805
} // namespace
791806

@@ -810,8 +825,8 @@ static void updateChildIncompleteness(const DWARFDie &Die, CompileUnit &CU,
810825
}
811826

812827
/// Helper that updates the completeness of the current DIE based on the
813-
/// completeness of the DIEs referencing it. It depends on the incompleteness
814-
/// of the referencing DIE already being computed.
828+
/// completeness of the DIEs it references. It depends on the incompleteness of
829+
/// the referenced DIE already being computed.
815830
static void updateRefIncompleteness(const DWARFDie &Die, CompileUnit &CU,
816831
CompileUnit::DIEInfo &RefInfo) {
817832
switch (Die.getTag()) {
@@ -835,6 +850,8 @@ static void updateRefIncompleteness(const DWARFDie &Die, CompileUnit &CU,
835850
MyInfo.Incomplete = true;
836851
}
837852

853+
/// Look at the children of the given DIE and decide whether they should be
854+
/// kept.
838855
static void lookForChildDIEsToKeep(const DWARFDie &Die, CompileUnit &CU,
839856
unsigned Flags,
840857
SmallVectorImpl<WorklistItem> &Worklist) {
@@ -846,6 +863,8 @@ static void lookForChildDIEsToKeep(const DWARFDie &Die, CompileUnit &CU,
846863
if (dieNeedsChildrenToBeMeaningful(Die.getTag()))
847864
Flags &= ~DwarfLinker::TF_ParentWalk;
848865

866+
// We're finished if this DIE has no children or we're walking the parent
867+
// chain.
849868
if (!Die.hasChildren() || (Flags & DwarfLinker::TF_ParentWalk))
850869
return;
851870

@@ -862,6 +881,94 @@ static void lookForChildDIEsToKeep(const DWARFDie &Die, CompileUnit &CU,
862881
}
863882
}
864883

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

9341060
// If it is a newly kept DIE mark it as well as all its dependencies as
9351061
// kept.
936-
bool UseOdr = (Current.Flags & TF_DependencyWalk)
937-
? (Current.Flags & TF_ODR)
938-
: Current.CU.hasODR();
939-
unsigned ODRFlag = UseOdr ? TF_ODR : 0;
940-
941-
DWARFUnit &Unit = Current.CU.getOrigUnit();
9421062
MyInfo.Keep = true;
9431063

9441064
// We're looking for incomplete types.
@@ -947,74 +1067,19 @@ void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
9471067
Current.Die.getTag() != dwarf::DW_TAG_member &&
9481068
dwarf::toUnsigned(Current.Die.find(dwarf::DW_AT_declaration), 0);
9491069

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

1000-
// Keep a module forward declaration if there is no definition.
1001-
if (!(isODRAttribute(AttrSpec.Attr) && Info.Ctxt &&
1002-
Info.Ctxt->getCanonicalDIEOffset()))
1003-
Info.Prune = false;
1004-
ReferencedDIEs.emplace_back(RefDie, *ReferencedCU);
1005-
}
1006-
}
1076+
bool UseOdr = (Current.Flags & TF_DependencyWalk) ? (Current.Flags & TF_ODR)
1077+
: Current.CU.hasODR();
1078+
unsigned ODRFlag = UseOdr ? TF_ODR : 0;
1079+
unsigned ParFlags = TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag;
10071080

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

0 commit comments

Comments
 (0)