1 //=== DependencyTracker.cpp -----------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "DependencyTracker.h"
10 #include "llvm/Support/FormatVariadic.h"
13 namespace dwarflinker_parallel
{
16 /// A broken link in the keep chain. By recording both the parent and the child
17 /// we can show only broken links for DIEs with multiple children.
19 BrokenLink(DWARFDie Parent
, DWARFDie Child
) : Parent(Parent
), Child(Child
) {}
24 /// Verify the keep chain by looking for DIEs that are kept but who's parent
26 void DependencyTracker::verifyKeepChain(CompileUnit
&CU
) {
27 SmallVector
<DWARFDie
> Worklist
;
28 Worklist
.push_back(CU
.getOrigUnit().getUnitDIE());
30 // List of broken links.
31 SmallVector
<BrokenLink
> BrokenLinks
;
33 while (!Worklist
.empty()) {
34 const DWARFDie Current
= Worklist
.back();
37 if (!Current
.isValid())
40 const bool CurrentDieIsKept
= CU
.getDIEInfo(Current
).getKeep() ||
41 CU
.getDIEInfo(Current
).getKeepChildren();
43 for (DWARFDie Child
: reverse(Current
.children())) {
44 Worklist
.push_back(Child
);
46 const bool ChildDieIsKept
= CU
.getDIEInfo(Child
).getKeep() ||
47 CU
.getDIEInfo(Child
).getKeepChildren();
48 if (!CurrentDieIsKept
&& ChildDieIsKept
)
49 BrokenLinks
.emplace_back(Current
, Child
);
53 if (!BrokenLinks
.empty()) {
54 for (BrokenLink Link
: BrokenLinks
) {
55 WithColor::error() << formatv(
56 "Found invalid link in keep chain between {0:x} and {1:x}\n",
57 Link
.Parent
.getOffset(), Link
.Child
.getOffset());
60 Link
.Parent
.dump(errs(), 0, {});
61 CU
.getDIEInfo(Link
.Parent
).dump();
64 Link
.Child
.dump(errs(), 2, {});
65 CU
.getDIEInfo(Link
.Child
).dump();
67 report_fatal_error("invalid keep chain");
72 bool DependencyTracker::resolveDependenciesAndMarkLiveness(CompileUnit
&CU
) {
73 // We do not track liveness inside Clang modules. We also do not track
74 // liveness if UpdateIndexTablesOnly is requested.
75 TrackLiveness
= !(CU
.isClangModule() ||
76 CU
.getGlobalData().getOptions().UpdateIndexTablesOnly
);
77 RootEntriesWorkList
.clear();
79 // Search for live root DIEs.
80 collectRootsToKeep(CU
, CU
.getDebugInfoEntry(0));
82 // Mark live DIEs as kept.
83 return markLiveRootsAsKept();
86 void DependencyTracker::collectRootsToKeep(CompileUnit
&CU
,
87 const DWARFDebugInfoEntry
*Entry
) {
89 addItemToWorklist(CU
, Entry
);
93 switch (Entry
->getTag()) {
94 case dwarf::DW_TAG_subprogram
:
95 case dwarf::DW_TAG_label
:
96 if (isLiveSubprogramEntry(CU
, Entry
)) {
97 addItemToWorklist(CU
, Entry
);
101 case dwarf::DW_TAG_compile_unit
:
102 case dwarf::DW_TAG_namespace
:
103 case dwarf::DW_TAG_module
:
104 case dwarf::DW_TAG_lexical_block
: {
105 for (const DWARFDebugInfoEntry
*CurChild
= CU
.getFirstChildEntry(Entry
);
106 CurChild
&& CurChild
->getAbbreviationDeclarationPtr();
107 CurChild
= CU
.getSiblingEntry(CurChild
))
108 collectRootsToKeep(CU
, CurChild
);
110 case dwarf::DW_TAG_constant
:
111 case dwarf::DW_TAG_variable
: {
112 if (isLiveVariableEntry(CU
, Entry
))
113 addItemToWorklist(CU
, Entry
);
115 case dwarf::DW_TAG_base_type
: {
116 addItemToWorklist(CU
, Entry
);
118 case dwarf::DW_TAG_imported_module
:
119 case dwarf::DW_TAG_imported_declaration
:
120 case dwarf::DW_TAG_imported_unit
: {
121 addItemToWorklist(CU
, Entry
);
129 bool DependencyTracker::markLiveRootsAsKept() {
132 while (!RootEntriesWorkList
.empty()) {
133 RootEntryTy CurrentItem
= RootEntriesWorkList
.pop_back_val();
135 if (!markDIEEntryAsKeptRec(CurrentItem
, CurrentItem
.CU
,
136 CurrentItem
.RootEntry
))
143 bool DependencyTracker::markDIEEntryAsKeptRec(
144 const RootEntryTy
&RootItem
, CompileUnit
&CU
,
145 const DWARFDebugInfoEntry
*Entry
) {
146 if (Entry
->getAbbreviationDeclarationPtr() == nullptr)
149 CompileUnit::DIEInfo
&Info
= CU
.getDIEInfo(Entry
);
154 // Mark parents as 'KeepChildren'.
155 std::optional
<uint32_t> ParentIdx
= Entry
->getParentIdx();
157 const DWARFDebugInfoEntry
*ParentEntry
= CU
.getDebugInfoEntry(*ParentIdx
);
158 CompileUnit::DIEInfo
&ParentInfo
= CU
.getDIEInfo(*ParentIdx
);
159 if (ParentInfo
.getKeepChildren())
161 ParentInfo
.setKeepChildren();
162 ParentIdx
= ParentEntry
->getParentIdx();
165 // Mark current DIE as kept.
167 setDIEPlacementAndTypename(Info
);
169 // Set liveness information.
170 switch (Entry
->getTag()) {
171 case dwarf::DW_TAG_constant
:
172 case dwarf::DW_TAG_variable
: {
173 isLiveVariableEntry(CU
, Entry
);
175 case dwarf::DW_TAG_subprogram
:
176 case dwarf::DW_TAG_label
: {
177 isLiveSubprogramEntry(CU
, Entry
);
184 // Analyse referenced DIEs.
186 if (!maybeAddReferencedRoots(RootItem
, CU
, Entry
))
189 // Navigate children.
190 for (const DWARFDebugInfoEntry
*CurChild
= CU
.getFirstChildEntry(Entry
);
191 CurChild
&& CurChild
->getAbbreviationDeclarationPtr();
192 CurChild
= CU
.getSiblingEntry(CurChild
)) {
193 if (!markDIEEntryAsKeptRec(RootItem
, CU
, CurChild
))
200 bool DependencyTracker::maybeAddReferencedRoots(
201 const RootEntryTy
&RootItem
, CompileUnit
&CU
,
202 const DWARFDebugInfoEntry
*Entry
) {
203 const auto *Abbrev
= Entry
->getAbbreviationDeclarationPtr();
204 if (Abbrev
== nullptr)
207 DWARFUnit
&Unit
= CU
.getOrigUnit();
208 DWARFDataExtractor Data
= Unit
.getDebugInfoExtractor();
209 uint64_t Offset
= Entry
->getOffset() + getULEB128Size(Abbrev
->getCode());
211 // For each DIE attribute...
212 for (const auto &AttrSpec
: Abbrev
->attributes()) {
213 DWARFFormValue
Val(AttrSpec
.Form
);
214 if (!Val
.isFormClass(DWARFFormValue::FC_Reference
) ||
215 AttrSpec
.Attr
== dwarf::DW_AT_sibling
) {
216 DWARFFormValue::skipValue(AttrSpec
.Form
, Data
, &Offset
,
217 Unit
.getFormParams());
220 Val
.extractValue(Data
, &Offset
, Unit
.getFormParams(), &Unit
);
222 // Resolve reference.
223 std::optional
<std::pair
<CompileUnit
*, uint32_t>> RefDie
=
224 CU
.resolveDIEReference(
225 Val
, Context
.InterCUProcessingStarted
226 ? ResolveInterCUReferencesMode::Resolve
227 : ResolveInterCUReferencesMode::AvoidResolving
);
229 CU
.warn("cann't find referenced DIE", Entry
);
233 if (RefDie
->second
== 0) {
234 // Delay resolving reference.
235 RefDie
->first
->setInterconnectedCU();
236 CU
.setInterconnectedCU();
237 Context
.HasNewInterconnectedCUs
= true;
241 assert(CU
.getUniqueID() == RefDie
->first
->getUniqueID() ||
242 Context
.InterCUProcessingStarted
);
244 addItemToWorklist(*RefDie
->first
,
245 RefDie
->first
->getDebugInfoEntry(RefDie
->second
));
251 // Returns true if the specified DIE type allows removing children.
252 static bool childrenCanBeRemoved(uint32_t Tag
) {
256 case dwarf::DW_TAG_class_type
:
257 case dwarf::DW_TAG_common_block
:
258 case dwarf::DW_TAG_lexical_block
:
259 case dwarf::DW_TAG_structure_type
:
260 case dwarf::DW_TAG_subprogram
:
261 case dwarf::DW_TAG_subroutine_type
:
262 case dwarf::DW_TAG_union_type
:
263 case dwarf::DW_TAG_array_type
:
266 llvm_unreachable("Invalid Tag");
269 void DependencyTracker::addItemToWorklist(CompileUnit
&CU
,
270 const DWARFDebugInfoEntry
*Entry
) {
271 if (Entry
->getAbbreviationDeclarationPtr() == nullptr)
274 const DWARFDebugInfoEntry
*EntryToAdd
= Entry
;
276 // If parent does not allow children removing then use that parent as a root
278 std::optional
<uint32_t> ParentIdx
= Entry
->getParentIdx();
280 const DWARFDebugInfoEntry
*ParentEntry
= CU
.getDebugInfoEntry(*ParentIdx
);
281 if (childrenCanBeRemoved(ParentEntry
->getTag()))
283 EntryToAdd
= ParentEntry
;
284 ParentIdx
= ParentEntry
->getParentIdx();
287 // Check if the DIE entry is already kept.
288 if (CU
.getDIEInfo(EntryToAdd
).getKeep())
291 RootEntriesWorkList
.emplace_back(CU
, EntryToAdd
);
294 bool DependencyTracker::isLiveVariableEntry(CompileUnit
&CU
,
295 const DWARFDebugInfoEntry
*Entry
) {
296 DWARFDie DIE
= CU
.getDIE(Entry
);
297 CompileUnit::DIEInfo
&Info
= CU
.getDIEInfo(DIE
);
300 const auto *Abbrev
= DIE
.getAbbreviationDeclarationPtr();
302 // Global variables with constant value can always be kept.
303 if (!Info
.getIsInFunctionScope() &&
304 Abbrev
->findAttributeIndex(dwarf::DW_AT_const_value
))
307 // See if there is a relocation to a valid debug map entry inside this
308 // variable's location. The order is important here. We want to always check
309 // if the variable has a location expression address.
310 // However, we don't want a static variable in a function to force us to
311 // keep the enclosing function, unless requested explicitly.
312 std::pair
<bool, std::optional
<int64_t>> LocExprAddrAndRelocAdjustment
=
313 CU
.getContaingFile().Addresses
->getVariableRelocAdjustment(DIE
);
315 if (!LocExprAddrAndRelocAdjustment
.second
)
318 if ((Info
.getIsInFunctionScope()) &&
319 !LLVM_UNLIKELY(CU
.getGlobalData().getOptions().KeepFunctionForStatic
))
323 if (CU
.getGlobalData().getOptions().Verbose
) {
324 outs() << "Keeping variable DIE:";
325 DIDumpOptions DumpOpts
;
326 DumpOpts
.ChildRecurseDepth
= 0;
327 DumpOpts
.Verbose
= CU
.getGlobalData().getOptions().Verbose
;
328 DIE
.dump(outs(), 8 /* Indent */, DumpOpts
);
334 bool DependencyTracker::isLiveSubprogramEntry(
335 CompileUnit
&CU
, const DWARFDebugInfoEntry
*Entry
) {
336 DWARFDie DIE
= CU
.getDIE(Entry
);
338 std::optional
<uint64_t> LowPc
;
339 std::optional
<uint64_t> HighPc
;
340 std::optional
<int64_t> RelocAdjustment
;
343 LowPc
= dwarf::toAddress(DIE
.find(dwarf::DW_AT_low_pc
));
348 CU
.getContaingFile().Addresses
->getSubprogramRelocAdjustment(DIE
);
349 if (!RelocAdjustment
)
352 if (DIE
.getTag() == dwarf::DW_TAG_subprogram
) {
353 // Validate subprogram address range.
355 HighPc
= DIE
.getHighPC(*LowPc
);
357 CU
.warn("function without high_pc. Range will be discarded.", &DIE
);
361 if (*LowPc
> *HighPc
) {
362 CU
.warn("low_pc greater than high_pc. Range will be discarded.", &DIE
);
365 } else if (DIE
.getTag() == dwarf::DW_TAG_variable
) {
366 if (CU
.hasLabelAt(*LowPc
))
369 // FIXME: dsymutil-classic compat. dsymutil-classic doesn't consider
370 // labels that don't fall into the CU's aranges. This is wrong IMO. Debug
371 // info generation bugs aside, this is really wrong in the case of labels,
372 // where a label marking the end of a function will have a PC == CU's
374 if (dwarf::toAddress(
375 CU
.getOrigUnit().getUnitDIE().find(dwarf::DW_AT_high_pc
))
376 .value_or(UINT64_MAX
) <= LowPc
)
379 CU
.addLabelLowPc(*LowPc
, *RelocAdjustment
);
383 if (CU
.getGlobalData().getOptions().Verbose
) {
384 outs() << "Keeping subprogram DIE:";
385 DIDumpOptions DumpOpts
;
386 DumpOpts
.ChildRecurseDepth
= 0;
387 DumpOpts
.Verbose
= CU
.getGlobalData().getOptions().Verbose
;
388 DIE
.dump(outs(), 8 /* Indent */, DumpOpts
);
391 if (!TrackLiveness
|| DIE
.getTag() == dwarf::DW_TAG_label
)
394 CU
.addFunctionRange(*LowPc
, *HighPc
, *RelocAdjustment
);
398 void DependencyTracker::setDIEPlacementAndTypename(CompileUnit::DIEInfo
&Info
) {
399 Info
.setPlacement(CompileUnit::PlainDwarf
);
402 } // end of namespace dwarflinker_parallel