1 //===- GenericCycleImpl.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 /// \file
10 /// This template implementation resides in a separate file so that it
11 /// does not get injected into every .cpp file that includes the
12 /// generic header.
13 ///
14 /// DO NOT INCLUDE THIS FILE WHEN MERELY USING CYCLEINFO.
15 ///
16 /// This file should only be included by files that implement a
17 /// specialization of the relevant templates. Currently these are:
18 /// - CycleAnalysis.cpp
19 /// - MachineCycleAnalysis.cpp
20 ///
21 //===----------------------------------------------------------------------===//
22
23 #ifndef LLVM_ADT_GENERICCYCLEIMPL_H
24 #define LLVM_ADT_GENERICCYCLEIMPL_H
25
26 #include "llvm/ADT/DenseSet.h"
27 #include "llvm/ADT/DepthFirstIterator.h"
28 #include "llvm/ADT/GenericCycleInfo.h"
29
30 #define DEBUG_TYPE "generic-cycle-impl"
31
32 namespace llvm {
33
34 template <typename ContextT>
contains(const GenericCycle * C)35 bool GenericCycle<ContextT>::contains(const GenericCycle *C) const {
36 if (!C)
37 return false;
38
39 if (Depth > C->Depth)
40 return false;
41 while (Depth < C->Depth)
42 C = C->ParentCycle;
43 return this == C;
44 }
45
46 template <typename ContextT>
getExitBlocks(SmallVectorImpl<BlockT * > & TmpStorage)47 void GenericCycle<ContextT>::getExitBlocks(
48 SmallVectorImpl<BlockT *> &TmpStorage) const {
49 TmpStorage.clear();
50
51 size_t NumExitBlocks = 0;
52 for (BlockT *Block : blocks()) {
53 llvm::append_range(TmpStorage, successors(Block));
54
55 for (size_t Idx = NumExitBlocks, End = TmpStorage.size(); Idx < End;
56 ++Idx) {
57 BlockT *Succ = TmpStorage[Idx];
58 if (!contains(Succ)) {
59 auto ExitEndIt = TmpStorage.begin() + NumExitBlocks;
60 if (std::find(TmpStorage.begin(), ExitEndIt, Succ) == ExitEndIt)
61 TmpStorage[NumExitBlocks++] = Succ;
62 }
63 }
64
65 TmpStorage.resize(NumExitBlocks);
66 }
67 }
68
69 template <typename ContextT>
70 auto GenericCycle<ContextT>::getCyclePreheader() const -> BlockT * {
71 BlockT *Predecessor = getCyclePredecessor();
72 if (!Predecessor)
73 return nullptr;
74
75 assert(isReducible() && "Cycle Predecessor must be in a reducible cycle!");
76
77 if (succ_size(Predecessor) != 1)
78 return nullptr;
79
80 // Make sure we are allowed to hoist instructions into the predecessor.
81 if (!Predecessor->isLegalToHoistInto())
82 return nullptr;
83
84 return Predecessor;
85 }
86
87 template <typename ContextT>
88 auto GenericCycle<ContextT>::getCyclePredecessor() const -> BlockT * {
89 if (!isReducible())
90 return nullptr;
91
92 BlockT *Out = nullptr;
93
94 // Loop over the predecessors of the header node...
95 BlockT *Header = getHeader();
96 for (const auto Pred : predecessors(Header)) {
97 if (!contains(Pred)) {
98 if (Out && Out != Pred)
99 return nullptr;
100 Out = Pred;
101 }
102 }
103
104 return Out;
105 }
106
107 /// \brief Helper class for computing cycle information.
108 template <typename ContextT> class GenericCycleInfoCompute {
109 using BlockT = typename ContextT::BlockT;
110 using CycleInfoT = GenericCycleInfo<ContextT>;
111 using CycleT = typename CycleInfoT::CycleT;
112
113 CycleInfoT &Info;
114
115 struct DFSInfo {
116 unsigned Start = 0; // DFS start; positive if block is found
117 unsigned End = 0; // DFS end
118
119 DFSInfo() = default;
DFSInfoDFSInfo120 explicit DFSInfo(unsigned Start) : Start(Start) {}
121
122 /// Whether this node is an ancestor (or equal to) the node \p Other
123 /// in the DFS tree.
isAncestorOfDFSInfo124 bool isAncestorOf(const DFSInfo &Other) const {
125 return Start <= Other.Start && Other.End <= End;
126 }
127 };
128
129 DenseMap<BlockT *, DFSInfo> BlockDFSInfo;
130 SmallVector<BlockT *, 8> BlockPreorder;
131
132 GenericCycleInfoCompute(const GenericCycleInfoCompute &) = delete;
133 GenericCycleInfoCompute &operator=(const GenericCycleInfoCompute &) = delete;
134
135 public:
GenericCycleInfoCompute(CycleInfoT & Info)136 GenericCycleInfoCompute(CycleInfoT &Info) : Info(Info) {}
137
138 void run(BlockT *EntryBlock);
139
140 static void updateDepth(CycleT *SubTree);
141
142 private:
143 void dfs(BlockT *EntryBlock);
144 };
145
146 template <typename ContextT>
147 auto GenericCycleInfo<ContextT>::getTopLevelParentCycle(BlockT *Block)
148 -> CycleT * {
149 auto Cycle = BlockMapTopLevel.find(Block);
150 if (Cycle != BlockMapTopLevel.end())
151 return Cycle->second;
152
153 auto MapIt = BlockMap.find(Block);
154 if (MapIt == BlockMap.end())
155 return nullptr;
156
157 auto *C = MapIt->second;
158 while (C->ParentCycle)
159 C = C->ParentCycle;
160 BlockMapTopLevel.try_emplace(Block, C);
161 return C;
162 }
163
164 template <typename ContextT>
moveTopLevelCycleToNewParent(CycleT * NewParent,CycleT * Child)165 void GenericCycleInfo<ContextT>::moveTopLevelCycleToNewParent(CycleT *NewParent,
166 CycleT *Child) {
167 assert((!Child->ParentCycle && !NewParent->ParentCycle) &&
168 "NewParent and Child must be both top level cycle!\n");
169 auto &CurrentContainer =
170 Child->ParentCycle ? Child->ParentCycle->Children : TopLevelCycles;
171 auto Pos = llvm::find_if(CurrentContainer, [=](const auto &Ptr) -> bool {
172 return Child == Ptr.get();
173 });
174 assert(Pos != CurrentContainer.end());
175 NewParent->Children.push_back(std::move(*Pos));
176 *Pos = std::move(CurrentContainer.back());
177 CurrentContainer.pop_back();
178 Child->ParentCycle = NewParent;
179
180 NewParent->Blocks.insert(NewParent->Blocks.end(), Child->block_begin(),
181 Child->block_end());
182
183 for (auto &It : BlockMapTopLevel)
184 if (It.second == Child)
185 It.second = NewParent;
186 }
187
188 /// \brief Main function of the cycle info computations.
189 template <typename ContextT>
run(BlockT * EntryBlock)190 void GenericCycleInfoCompute<ContextT>::run(BlockT *EntryBlock) {
191 LLVM_DEBUG(errs() << "Entry block: " << Info.Context.print(EntryBlock)
192 << "\n");
193 dfs(EntryBlock);
194
195 SmallVector<BlockT *, 8> Worklist;
196
197 for (BlockT *HeaderCandidate : llvm::reverse(BlockPreorder)) {
198 const DFSInfo CandidateInfo = BlockDFSInfo.lookup(HeaderCandidate);
199
200 for (BlockT *Pred : predecessors(HeaderCandidate)) {
201 const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred);
202 if (CandidateInfo.isAncestorOf(PredDFSInfo))
203 Worklist.push_back(Pred);
204 }
205 if (Worklist.empty()) {
206 continue;
207 }
208
209 // Found a cycle with the candidate as its header.
210 LLVM_DEBUG(errs() << "Found cycle for header: "
211 << Info.Context.print(HeaderCandidate) << "\n");
212 std::unique_ptr<CycleT> NewCycle = std::make_unique<CycleT>();
213 NewCycle->appendEntry(HeaderCandidate);
214 NewCycle->appendBlock(HeaderCandidate);
215 Info.BlockMap.try_emplace(HeaderCandidate, NewCycle.get());
216
217 // Helper function to process (non-back-edge) predecessors of a discovered
218 // block and either add them to the worklist or recognize that the given
219 // block is an additional cycle entry.
220 auto ProcessPredecessors = [&](BlockT *Block) {
221 LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": ");
222
223 bool IsEntry = false;
224 for (BlockT *Pred : predecessors(Block)) {
225 const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred);
226 if (CandidateInfo.isAncestorOf(PredDFSInfo)) {
227 Worklist.push_back(Pred);
228 } else {
229 IsEntry = true;
230 }
231 }
232 if (IsEntry) {
233 assert(!NewCycle->isEntry(Block));
234 LLVM_DEBUG(errs() << "append as entry\n");
235 NewCycle->appendEntry(Block);
236 } else {
237 LLVM_DEBUG(errs() << "append as child\n");
238 }
239 };
240
241 do {
242 BlockT *Block = Worklist.pop_back_val();
243 if (Block == HeaderCandidate)
244 continue;
245
246 // If the block has already been discovered by some cycle
247 // (possibly by ourself), then the outermost cycle containing it
248 // should become our child.
249 if (auto *BlockParent = Info.getTopLevelParentCycle(Block)) {
250 LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": ");
251
252 if (BlockParent != NewCycle.get()) {
253 LLVM_DEBUG(errs()
254 << "discovered child cycle "
255 << Info.Context.print(BlockParent->getHeader()) << "\n");
256 // Make BlockParent the child of NewCycle.
257 Info.moveTopLevelCycleToNewParent(NewCycle.get(), BlockParent);
258
259 for (auto *ChildEntry : BlockParent->entries())
260 ProcessPredecessors(ChildEntry);
261 } else {
262 LLVM_DEBUG(errs()
263 << "known child cycle "
264 << Info.Context.print(BlockParent->getHeader()) << "\n");
265 }
266 } else {
267 Info.BlockMap.try_emplace(Block, NewCycle.get());
268 assert(!is_contained(NewCycle->Blocks, Block));
269 NewCycle->Blocks.push_back(Block);
270 ProcessPredecessors(Block);
271 Info.BlockMapTopLevel.try_emplace(Block, NewCycle.get());
272 }
273 } while (!Worklist.empty());
274
275 Info.TopLevelCycles.push_back(std::move(NewCycle));
276 }
277
278 // Fix top-level cycle links and compute cycle depths.
279 for (auto *TLC : Info.toplevel_cycles()) {
280 LLVM_DEBUG(errs() << "top-level cycle: "
281 << Info.Context.print(TLC->getHeader()) << "\n");
282
283 TLC->ParentCycle = nullptr;
284 updateDepth(TLC);
285 }
286 }
287
288 /// \brief Recompute depth values of \p SubTree and all descendants.
289 template <typename ContextT>
updateDepth(CycleT * SubTree)290 void GenericCycleInfoCompute<ContextT>::updateDepth(CycleT *SubTree) {
291 for (CycleT *Cycle : depth_first(SubTree))
292 Cycle->Depth = Cycle->ParentCycle ? Cycle->ParentCycle->Depth + 1 : 1;
293 }
294
295 /// \brief Compute a DFS of basic blocks starting at the function entry.
296 ///
297 /// Fills BlockDFSInfo with start/end counters and BlockPreorder.
298 template <typename ContextT>
dfs(BlockT * EntryBlock)299 void GenericCycleInfoCompute<ContextT>::dfs(BlockT *EntryBlock) {
300 SmallVector<unsigned, 8> DFSTreeStack;
301 SmallVector<BlockT *, 8> TraverseStack;
302 unsigned Counter = 0;
303 TraverseStack.emplace_back(EntryBlock);
304
305 do {
306 BlockT *Block = TraverseStack.back();
307 LLVM_DEBUG(errs() << "DFS visiting block: " << Info.Context.print(Block)
308 << "\n");
309 if (!BlockDFSInfo.count(Block)) {
310 // We're visiting the block for the first time. Open its DFSInfo, add
311 // successors to the traversal stack, and remember the traversal stack
312 // depth at which the block was opened, so that we can correctly record
313 // its end time.
314 LLVM_DEBUG(errs() << " first encountered at depth "
315 << TraverseStack.size() << "\n");
316
317 DFSTreeStack.emplace_back(TraverseStack.size());
318 llvm::append_range(TraverseStack, successors(Block));
319
320 bool Added = BlockDFSInfo.try_emplace(Block, ++Counter).second;
321 (void)Added;
322 assert(Added);
323 BlockPreorder.push_back(Block);
324 LLVM_DEBUG(errs() << " preorder number: " << Counter << "\n");
325 } else {
326 assert(!DFSTreeStack.empty());
327 if (DFSTreeStack.back() == TraverseStack.size()) {
328 LLVM_DEBUG(errs() << " ended at " << Counter << "\n");
329 BlockDFSInfo.find(Block)->second.End = Counter;
330 DFSTreeStack.pop_back();
331 } else {
332 LLVM_DEBUG(errs() << " already done\n");
333 }
334 TraverseStack.pop_back();
335 }
336 } while (!TraverseStack.empty());
337 assert(DFSTreeStack.empty());
338
339 LLVM_DEBUG(
340 errs() << "Preorder:\n";
341 for (int i = 0, e = BlockPreorder.size(); i != e; ++i) {
342 errs() << " " << Info.Context.print(BlockPreorder[i]) << ": " << i << "\n";
343 }
344 );
345 }
346
347 /// \brief Reset the object to its initial state.
clear()348 template <typename ContextT> void GenericCycleInfo<ContextT>::clear() {
349 TopLevelCycles.clear();
350 BlockMap.clear();
351 BlockMapTopLevel.clear();
352 }
353
354 /// \brief Compute the cycle info for a function.
355 template <typename ContextT>
compute(FunctionT & F)356 void GenericCycleInfo<ContextT>::compute(FunctionT &F) {
357 GenericCycleInfoCompute<ContextT> Compute(*this);
358 Context.setFunction(F);
359
360 LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName()
361 << "\n");
362 Compute.run(ContextT::getEntryBlock(F));
363
364 assert(validateTree());
365 }
366
367 /// \brief Find the innermost cycle containing a given block.
368 ///
369 /// \returns the innermost cycle containing \p Block or nullptr if
370 /// it is not contained in any cycle.
371 template <typename ContextT>
372 auto GenericCycleInfo<ContextT>::getCycle(const BlockT *Block) const
373 -> CycleT * {
374 auto MapIt = BlockMap.find(Block);
375 if (MapIt != BlockMap.end())
376 return MapIt->second;
377 return nullptr;
378 }
379
380 /// \brief get the depth for the cycle which containing a given block.
381 ///
382 /// \returns the depth for the innermost cycle containing \p Block or 0 if it is
383 /// not contained in any cycle.
384 template <typename ContextT>
getCycleDepth(const BlockT * Block)385 unsigned GenericCycleInfo<ContextT>::getCycleDepth(const BlockT *Block) const {
386 CycleT *Cycle = getCycle(Block);
387 if (!Cycle)
388 return 0;
389 return Cycle->getDepth();
390 }
391
392 #ifndef NDEBUG
393 /// \brief Validate the internal consistency of the cycle tree.
394 ///
395 /// Note that this does \em not check that cycles are really cycles in the CFG,
396 /// or that the right set of cycles in the CFG were found.
397 template <typename ContextT>
validateTree()398 bool GenericCycleInfo<ContextT>::validateTree() const {
399 DenseSet<BlockT *> Blocks;
400 DenseSet<BlockT *> Entries;
401
402 auto reportError = [](const char *File, int Line, const char *Cond) {
403 errs() << File << ':' << Line
404 << ": GenericCycleInfo::validateTree: " << Cond << '\n';
405 };
406 #define check(cond) \
407 do { \
408 if (!(cond)) { \
409 reportError(__FILE__, __LINE__, #cond); \
410 return false; \
411 } \
412 } while (false)
413
414 for (const auto *TLC : toplevel_cycles()) {
415 for (const CycleT *Cycle : depth_first(TLC)) {
416 if (Cycle->ParentCycle)
417 check(is_contained(Cycle->ParentCycle->children(), Cycle));
418
419 for (BlockT *Block : Cycle->Blocks) {
420 auto MapIt = BlockMap.find(Block);
421 check(MapIt != BlockMap.end());
422 check(Cycle->contains(MapIt->second));
423 check(Blocks.insert(Block).second); // duplicates in block list?
424 }
425 Blocks.clear();
426
427 check(!Cycle->Entries.empty());
428 for (BlockT *Entry : Cycle->Entries) {
429 check(Entries.insert(Entry).second); // duplicate entry?
430 check(is_contained(Cycle->Blocks, Entry));
431 }
432 Entries.clear();
433
434 unsigned ChildDepth = 0;
435 for (const CycleT *Child : Cycle->children()) {
436 check(Child->Depth > Cycle->Depth);
437 if (!ChildDepth) {
438 ChildDepth = Child->Depth;
439 } else {
440 check(ChildDepth == Child->Depth);
441 }
442 }
443 }
444 }
445
446 for (const auto &Entry : BlockMap) {
447 BlockT *Block = Entry.first;
448 for (const CycleT *Cycle = Entry.second; Cycle;
449 Cycle = Cycle->ParentCycle) {
450 check(is_contained(Cycle->Blocks, Block));
451 }
452 }
453
454 #undef check
455
456 return true;
457 }
458 #endif
459
460 /// \brief Print the cycle info.
461 template <typename ContextT>
print(raw_ostream & Out)462 void GenericCycleInfo<ContextT>::print(raw_ostream &Out) const {
463 for (const auto *TLC : toplevel_cycles()) {
464 for (const CycleT *Cycle : depth_first(TLC)) {
465 for (unsigned I = 0; I < Cycle->Depth; ++I)
466 Out << " ";
467
468 Out << Cycle->print(Context) << '\n';
469 }
470 }
471 }
472
473 } // namespace llvm
474
475 #undef DEBUG_TYPE
476
477 #endif // LLVM_ADT_GENERICCYCLEIMPL_H
478