| 1 | //===- "DependencyTracker.h" ------------------------------------*- C++ -*-===// |
| 2 | // |
| 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 |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLVM_LIB_DWARFLINKER_PARALLEL_DEPENDENCYTRACKER_H |
| 10 | #define LLVM_LIB_DWARFLINKER_PARALLEL_DEPENDENCYTRACKER_H |
| 11 | |
| 12 | #include "DWARFLinkerCompileUnit.h" |
| 13 | #include "llvm/ADT/PointerIntPair.h" |
| 14 | #include "llvm/ADT/SmallVector.h" |
| 15 | |
| 16 | namespace llvm { |
| 17 | class DWARFDebugInfoEntry; |
| 18 | class DWARFDie; |
| 19 | |
| 20 | namespace dwarf_linker { |
| 21 | namespace parallel { |
| 22 | |
| 23 | /// This class discovers DIEs dependencies: marks "live" DIEs, marks DIE |
| 24 | /// locations (whether DIE should be cloned as regular DIE or it should be put |
| 25 | /// into the artificial type unit). |
| 26 | class DependencyTracker { |
| 27 | public: |
| 28 | DependencyTracker(CompileUnit &CU) : CU(CU) {} |
| 29 | |
| 30 | /// Recursively walk the \p DIE tree and look for DIEs to keep. Store that |
| 31 | /// information in \p CU's DIEInfo. |
| 32 | /// |
| 33 | /// This function is the entry point of the DIE selection algorithm. It is |
| 34 | /// expected to walk the DIE tree and(through the mediation of |
| 35 | /// Context.File.Addresses) ask for relocation adjustment value on each |
| 36 | /// DIE that might be a 'root DIE'(f.e. subprograms, variables). |
| 37 | /// |
| 38 | /// Returns true if all dependencies are correctly discovered. Inter-CU |
| 39 | /// dependencies cannot be discovered if referenced CU is not analyzed yet. |
| 40 | /// If that is the case this method returns false. |
| 41 | bool resolveDependenciesAndMarkLiveness( |
| 42 | bool InterCUProcessingStarted, |
| 43 | std::atomic<bool> &HasNewInterconnectedCUs); |
| 44 | |
| 45 | /// Check if dependencies have incompatible placement. |
| 46 | /// If that is the case modify placement to be compatible. |
| 47 | /// \returns true if any placement was updated, otherwise returns false. |
| 48 | /// This method should be called as a followup processing after |
| 49 | /// resolveDependenciesAndMarkLiveness(). |
| 50 | bool updateDependenciesCompleteness(); |
| 51 | |
| 52 | /// Recursively walk the \p DIE tree and check "keepness" and "placement" |
| 53 | /// information. It is an error if parent node does not have "keep" flag, |
| 54 | /// while child has one. It is an error if parent node has "TypeTable" |
| 55 | /// placement while child has "PlainDwarf" placement. This function dump error |
| 56 | /// at stderr in that case. |
| 57 | void verifyKeepChain(); |
| 58 | |
| 59 | protected: |
| 60 | enum class LiveRootWorklistActionTy : uint8_t { |
| 61 | /// Mark current item as live entry. |
| 62 | MarkSingleLiveEntry = 0, |
| 63 | |
| 64 | /// Mark current item as type entry. |
| 65 | MarkSingleTypeEntry, |
| 66 | |
| 67 | /// Mark current item and all its children as live entry. |
| 68 | MarkLiveEntryRec, |
| 69 | |
| 70 | /// Mark current item and all its children as type entry. |
| 71 | MarkTypeEntryRec, |
| 72 | |
| 73 | /// Mark all children of current item as live entry. |
| 74 | MarkLiveChildrenRec, |
| 75 | |
| 76 | /// Mark all children of current item as type entry. |
| 77 | MarkTypeChildrenRec, |
| 78 | }; |
| 79 | |
| 80 | /// \returns true if the specified action is for the "PlainDwarf". |
| 81 | bool isLiveAction(LiveRootWorklistActionTy Action) { |
| 82 | switch (Action) { |
| 83 | default: |
| 84 | return false; |
| 85 | |
| 86 | case LiveRootWorklistActionTy::MarkSingleLiveEntry: |
| 87 | case LiveRootWorklistActionTy::MarkLiveEntryRec: |
| 88 | case LiveRootWorklistActionTy::MarkLiveChildrenRec: |
| 89 | return true; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | /// \returns true if the specified action is for the "TypeTable". |
| 94 | bool isTypeAction(LiveRootWorklistActionTy Action) { |
| 95 | switch (Action) { |
| 96 | default: |
| 97 | return false; |
| 98 | |
| 99 | case LiveRootWorklistActionTy::MarkSingleTypeEntry: |
| 100 | case LiveRootWorklistActionTy::MarkTypeEntryRec: |
| 101 | case LiveRootWorklistActionTy::MarkTypeChildrenRec: |
| 102 | return true; |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | /// \returns true if the specified action affects only Root entry |
| 107 | /// itself and does not affect it`s children. |
| 108 | bool isSingleAction(LiveRootWorklistActionTy Action) { |
| 109 | switch (Action) { |
| 110 | default: |
| 111 | return false; |
| 112 | |
| 113 | case LiveRootWorklistActionTy::MarkSingleLiveEntry: |
| 114 | case LiveRootWorklistActionTy::MarkSingleTypeEntry: |
| 115 | return true; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | /// \returns true if the specified action affects only Root entry |
| 120 | /// itself and does not affect it`s children. |
| 121 | bool isChildrenAction(LiveRootWorklistActionTy Action) { |
| 122 | switch (Action) { |
| 123 | default: |
| 124 | return false; |
| 125 | |
| 126 | case LiveRootWorklistActionTy::MarkLiveChildrenRec: |
| 127 | case LiveRootWorklistActionTy::MarkTypeChildrenRec: |
| 128 | return true; |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | /// Class keeping live worklist item data. |
| 133 | class LiveRootWorklistItemTy { |
| 134 | public: |
| 135 | LiveRootWorklistItemTy() = default; |
| 136 | LiveRootWorklistItemTy(const LiveRootWorklistItemTy &) = default; |
| 137 | LiveRootWorklistItemTy(LiveRootWorklistActionTy Action, |
| 138 | UnitEntryPairTy RootEntry) { |
| 139 | RootCU.setInt(Action); |
| 140 | RootCU.setPointer(RootEntry.CU); |
| 141 | |
| 142 | RootDieEntry = RootEntry.DieEntry; |
| 143 | } |
| 144 | LiveRootWorklistItemTy(LiveRootWorklistActionTy Action, |
| 145 | UnitEntryPairTy RootEntry, |
| 146 | UnitEntryPairTy ReferencedBy) { |
| 147 | RootCU.setPointer(RootEntry.CU); |
| 148 | RootCU.setInt(Action); |
| 149 | RootDieEntry = RootEntry.DieEntry; |
| 150 | |
| 151 | ReferencedByCU = ReferencedBy.CU; |
| 152 | ReferencedByDieEntry = ReferencedBy.DieEntry; |
| 153 | } |
| 154 | |
| 155 | UnitEntryPairTy getRootEntry() const { |
| 156 | return UnitEntryPairTy{RootCU.getPointer(), RootDieEntry}; |
| 157 | } |
| 158 | |
| 159 | CompileUnit::DieOutputPlacement getPlacement() const { |
| 160 | return static_cast<CompileUnit::DieOutputPlacement>(RootCU.getInt()); |
| 161 | } |
| 162 | |
| 163 | bool hasReferencedByOtherEntry() const { return ReferencedByCU != nullptr; } |
| 164 | |
| 165 | UnitEntryPairTy getReferencedByEntry() const { |
| 166 | assert(ReferencedByCU); |
| 167 | assert(ReferencedByDieEntry); |
| 168 | return UnitEntryPairTy{ReferencedByCU, ReferencedByDieEntry}; |
| 169 | } |
| 170 | |
| 171 | LiveRootWorklistActionTy getAction() const { |
| 172 | return static_cast<LiveRootWorklistActionTy>(RootCU.getInt()); |
| 173 | } |
| 174 | |
| 175 | protected: |
| 176 | /// Root entry. |
| 177 | /// ASSUMPTION: 3 bits are used to store LiveRootWorklistActionTy value. |
| 178 | /// Thus LiveRootWorklistActionTy should have no more eight elements. |
| 179 | |
| 180 | /// Pointer traits for CompileUnit. |
| 181 | struct CompileUnitPointerTraits { |
| 182 | static inline void *getAsVoidPointer(CompileUnit *P) { return P; } |
| 183 | static inline CompileUnit *getFromVoidPointer(void *P) { |
| 184 | return (CompileUnit *)P; |
| 185 | } |
| 186 | static constexpr int NumLowBitsAvailable = 3; |
| 187 | static_assert( |
| 188 | alignof(CompileUnit) >= (1 << NumLowBitsAvailable), |
| 189 | "CompileUnit insufficiently aligned to have enough low bits." ); |
| 190 | }; |
| 191 | |
| 192 | PointerIntPair<CompileUnit *, 3, LiveRootWorklistActionTy, |
| 193 | CompileUnitPointerTraits> |
| 194 | RootCU; |
| 195 | const DWARFDebugInfoEntry *RootDieEntry = nullptr; |
| 196 | |
| 197 | /// Another root entry which references this RootDieEntry. |
| 198 | /// ReferencedByDieEntry is kept to update placement. |
| 199 | /// if RootDieEntry has placement incompatible with placement |
| 200 | /// of ReferencedByDieEntry then it should be updated. |
| 201 | CompileUnit *ReferencedByCU = nullptr; |
| 202 | const DWARFDebugInfoEntry *ReferencedByDieEntry = nullptr; |
| 203 | }; |
| 204 | |
| 205 | using RootEntriesListTy = SmallVector<LiveRootWorklistItemTy>; |
| 206 | |
| 207 | /// This function navigates DIEs tree starting from specified \p Entry. |
| 208 | /// It puts found 'root DIE' into the worklist. The \p CollectLiveEntries |
| 209 | /// instructs to collect either live roots(like subprograms having live |
| 210 | /// DW_AT_low_pc) or otherwise roots which is not live(they need to be |
| 211 | /// collected if they are imported f.e. by DW_TAG_imported_module). |
| 212 | void collectRootsToKeep(const UnitEntryPairTy &Entry, |
| 213 | std::optional<UnitEntryPairTy> ReferencedBy, |
| 214 | bool IsLiveParent); |
| 215 | |
| 216 | /// Returns true if specified variable references live code section. |
| 217 | static bool isLiveVariableEntry(const UnitEntryPairTy &Entry, |
| 218 | bool IsLiveParent); |
| 219 | |
| 220 | /// Returns true if specified subprogram references live code section. |
| 221 | static bool isLiveSubprogramEntry(const UnitEntryPairTy &Entry); |
| 222 | |
| 223 | /// Examine worklist and mark all 'root DIE's as kept and set "Placement" |
| 224 | /// property. |
| 225 | bool markCollectedLiveRootsAsKept(bool InterCUProcessingStarted, |
| 226 | std::atomic<bool> &HasNewInterconnectedCUs); |
| 227 | |
| 228 | /// Mark whole DIE tree as kept recursively. |
| 229 | bool markDIEEntryAsKeptRec(LiveRootWorklistActionTy Action, |
| 230 | const UnitEntryPairTy &RootEntry, |
| 231 | const UnitEntryPairTy &Entry, |
| 232 | bool InterCUProcessingStarted, |
| 233 | std::atomic<bool> &HasNewInterconnectedCUs); |
| 234 | |
| 235 | /// Mark parents as keeping children. |
| 236 | void markParentsAsKeepingChildren(const UnitEntryPairTy &Entry); |
| 237 | |
| 238 | /// Mark whole DIE tree as placed in "PlainDwarf". |
| 239 | void setPlainDwarfPlacementRec(const UnitEntryPairTy &Entry); |
| 240 | |
| 241 | /// Check referenced DIEs and add them into the worklist. |
| 242 | bool maybeAddReferencedRoots(LiveRootWorklistActionTy Action, |
| 243 | const UnitEntryPairTy &RootEntry, |
| 244 | const UnitEntryPairTy &Entry, |
| 245 | bool InterCUProcessingStarted, |
| 246 | std::atomic<bool> &HasNewInterconnectedCUs); |
| 247 | |
| 248 | /// \returns true if \p DIEEntry can possibly be put into the artificial type |
| 249 | /// unit. |
| 250 | bool isTypeTableCandidate(const DWARFDebugInfoEntry *DIEEntry); |
| 251 | |
| 252 | /// \returns root for the specified \p Entry. |
| 253 | UnitEntryPairTy getRootForSpecifiedEntry(UnitEntryPairTy Entry); |
| 254 | |
| 255 | /// Add action item to the work list. |
| 256 | void |
| 257 | addActionToRootEntriesWorkList(LiveRootWorklistActionTy Action, |
| 258 | const UnitEntryPairTy &Entry, |
| 259 | std::optional<UnitEntryPairTy> ReferencedBy); |
| 260 | |
| 261 | CompileUnit &CU; |
| 262 | |
| 263 | /// List of entries which are 'root DIE's. |
| 264 | RootEntriesListTy RootEntriesWorkList; |
| 265 | |
| 266 | /// List of entries dependencies. |
| 267 | RootEntriesListTy Dependencies; |
| 268 | }; |
| 269 | |
| 270 | } // end of namespace parallel |
| 271 | } // end of namespace dwarf_linker |
| 272 | } // end of namespace llvm |
| 273 | |
| 274 | #endif // LLVM_LIB_DWARFLINKER_PARALLEL_DEPENDENCYTRACKER_H |
| 275 | |