xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ADT/GenericCycleImpl.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 /// - llvm/lib/IR/CycleInfo.cpp
19 /// - llvm/lib/CodeGen/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>
getExitingBlocks(SmallVectorImpl<BlockT * > & TmpStorage)70 void GenericCycle<ContextT>::getExitingBlocks(
71     SmallVectorImpl<BlockT *> &TmpStorage) const {
72   TmpStorage.clear();
73 
74   for (BlockT *Block : blocks()) {
75     for (BlockT *Succ : successors(Block)) {
76       if (!contains(Succ)) {
77         TmpStorage.push_back(Block);
78         break;
79       }
80     }
81   }
82 }
83 
84 template <typename ContextT>
85 auto GenericCycle<ContextT>::getCyclePreheader() const -> BlockT * {
86   BlockT *Predecessor = getCyclePredecessor();
87   if (!Predecessor)
88     return nullptr;
89 
90   assert(isReducible() && "Cycle Predecessor must be in a reducible cycle!");
91 
92   if (succ_size(Predecessor) != 1)
93     return nullptr;
94 
95   // Make sure we are allowed to hoist instructions into the predecessor.
96   if (!Predecessor->isLegalToHoistInto())
97     return nullptr;
98 
99   return Predecessor;
100 }
101 
102 template <typename ContextT>
103 auto GenericCycle<ContextT>::getCyclePredecessor() const -> BlockT * {
104   if (!isReducible())
105     return nullptr;
106 
107   BlockT *Out = nullptr;
108 
109   // Loop over the predecessors of the header node...
110   BlockT *Header = getHeader();
111   for (const auto Pred : predecessors(Header)) {
112     if (!contains(Pred)) {
113       if (Out && Out != Pred)
114         return nullptr;
115       Out = Pred;
116     }
117   }
118 
119   return Out;
120 }
121 
122 /// \brief Helper class for computing cycle information.
123 template <typename ContextT> class GenericCycleInfoCompute {
124   using BlockT = typename ContextT::BlockT;
125   using CycleInfoT = GenericCycleInfo<ContextT>;
126   using CycleT = typename CycleInfoT::CycleT;
127 
128   CycleInfoT &Info;
129 
130   struct DFSInfo {
131     unsigned Start = 0; // DFS start; positive if block is found
132     unsigned End = 0;   // DFS end
133 
134     DFSInfo() = default;
DFSInfoDFSInfo135     explicit DFSInfo(unsigned Start) : Start(Start) {}
136 
137     /// Whether this node is an ancestor (or equal to) the node \p Other
138     /// in the DFS tree.
isAncestorOfDFSInfo139     bool isAncestorOf(const DFSInfo &Other) const {
140       return Start <= Other.Start && Other.End <= End;
141     }
142   };
143 
144   DenseMap<BlockT *, DFSInfo> BlockDFSInfo;
145   SmallVector<BlockT *, 8> BlockPreorder;
146 
147   GenericCycleInfoCompute(const GenericCycleInfoCompute &) = delete;
148   GenericCycleInfoCompute &operator=(const GenericCycleInfoCompute &) = delete;
149 
150 public:
GenericCycleInfoCompute(CycleInfoT & Info)151   GenericCycleInfoCompute(CycleInfoT &Info) : Info(Info) {}
152 
153   void run(BlockT *EntryBlock);
154 
155   static void updateDepth(CycleT *SubTree);
156 
157 private:
158   void dfs(BlockT *EntryBlock);
159 };
160 
161 template <typename ContextT>
162 auto GenericCycleInfo<ContextT>::getTopLevelParentCycle(BlockT *Block)
163     -> CycleT * {
164   auto Cycle = BlockMapTopLevel.find(Block);
165   if (Cycle != BlockMapTopLevel.end())
166     return Cycle->second;
167 
168   auto MapIt = BlockMap.find(Block);
169   if (MapIt == BlockMap.end())
170     return nullptr;
171 
172   auto *C = MapIt->second;
173   while (C->ParentCycle)
174     C = C->ParentCycle;
175   BlockMapTopLevel.try_emplace(Block, C);
176   return C;
177 }
178 
179 template <typename ContextT>
moveTopLevelCycleToNewParent(CycleT * NewParent,CycleT * Child)180 void GenericCycleInfo<ContextT>::moveTopLevelCycleToNewParent(CycleT *NewParent,
181                                                               CycleT *Child) {
182   assert((!Child->ParentCycle && !NewParent->ParentCycle) &&
183          "NewParent and Child must be both top level cycle!\n");
184   auto &CurrentContainer =
185       Child->ParentCycle ? Child->ParentCycle->Children : TopLevelCycles;
186   auto Pos = llvm::find_if(CurrentContainer, [=](const auto &Ptr) -> bool {
187     return Child == Ptr.get();
188   });
189   assert(Pos != CurrentContainer.end());
190   NewParent->Children.push_back(std::move(*Pos));
191   *Pos = std::move(CurrentContainer.back());
192   CurrentContainer.pop_back();
193   Child->ParentCycle = NewParent;
194 
195   NewParent->Blocks.insert(Child->block_begin(), Child->block_end());
196 
197   for (auto &It : BlockMapTopLevel)
198     if (It.second == Child)
199       It.second = NewParent;
200 }
201 
202 template <typename ContextT>
addBlockToCycle(BlockT * Block,CycleT * Cycle)203 void GenericCycleInfo<ContextT>::addBlockToCycle(BlockT *Block, CycleT *Cycle) {
204   // FixMe: Appending NewBlock is fine as a set of blocks in a cycle. When
205   // printing, cycle NewBlock is at the end of list but it should be in the
206   // middle to represent actual traversal of a cycle.
207   Cycle->appendBlock(Block);
208   BlockMap.try_emplace(Block, Cycle);
209 
210   CycleT *ParentCycle = Cycle->getParentCycle();
211   while (ParentCycle) {
212     Cycle = ParentCycle;
213     Cycle->appendBlock(Block);
214     ParentCycle = Cycle->getParentCycle();
215   }
216 
217   BlockMapTopLevel.try_emplace(Block, Cycle);
218 }
219 
220 /// \brief Main function of the cycle info computations.
221 template <typename ContextT>
run(BlockT * EntryBlock)222 void GenericCycleInfoCompute<ContextT>::run(BlockT *EntryBlock) {
223   LLVM_DEBUG(errs() << "Entry block: " << Info.Context.print(EntryBlock)
224                     << "\n");
225   dfs(EntryBlock);
226 
227   SmallVector<BlockT *, 8> Worklist;
228 
229   for (BlockT *HeaderCandidate : llvm::reverse(BlockPreorder)) {
230     const DFSInfo CandidateInfo = BlockDFSInfo.lookup(HeaderCandidate);
231 
232     for (BlockT *Pred : predecessors(HeaderCandidate)) {
233       const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred);
234       if (CandidateInfo.isAncestorOf(PredDFSInfo))
235         Worklist.push_back(Pred);
236     }
237     if (Worklist.empty()) {
238       continue;
239     }
240 
241     // Found a cycle with the candidate as its header.
242     LLVM_DEBUG(errs() << "Found cycle for header: "
243                       << Info.Context.print(HeaderCandidate) << "\n");
244     std::unique_ptr<CycleT> NewCycle = std::make_unique<CycleT>();
245     NewCycle->appendEntry(HeaderCandidate);
246     NewCycle->appendBlock(HeaderCandidate);
247     Info.BlockMap.try_emplace(HeaderCandidate, NewCycle.get());
248 
249     // Helper function to process (non-back-edge) predecessors of a discovered
250     // block and either add them to the worklist or recognize that the given
251     // block is an additional cycle entry.
252     auto ProcessPredecessors = [&](BlockT *Block) {
253       LLVM_DEBUG(errs() << "  block " << Info.Context.print(Block) << ": ");
254 
255       bool IsEntry = false;
256       for (BlockT *Pred : predecessors(Block)) {
257         const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred);
258         if (CandidateInfo.isAncestorOf(PredDFSInfo)) {
259           Worklist.push_back(Pred);
260         } else {
261           IsEntry = true;
262         }
263       }
264       if (IsEntry) {
265         assert(!NewCycle->isEntry(Block));
266         LLVM_DEBUG(errs() << "append as entry\n");
267         NewCycle->appendEntry(Block);
268       } else {
269         LLVM_DEBUG(errs() << "append as child\n");
270       }
271     };
272 
273     do {
274       BlockT *Block = Worklist.pop_back_val();
275       if (Block == HeaderCandidate)
276         continue;
277 
278       // If the block has already been discovered by some cycle
279       // (possibly by ourself), then the outermost cycle containing it
280       // should become our child.
281       if (auto *BlockParent = Info.getTopLevelParentCycle(Block)) {
282         LLVM_DEBUG(errs() << "  block " << Info.Context.print(Block) << ": ");
283 
284         if (BlockParent != NewCycle.get()) {
285           LLVM_DEBUG(errs()
286                      << "discovered child cycle "
287                      << Info.Context.print(BlockParent->getHeader()) << "\n");
288           // Make BlockParent the child of NewCycle.
289           Info.moveTopLevelCycleToNewParent(NewCycle.get(), BlockParent);
290 
291           for (auto *ChildEntry : BlockParent->entries())
292             ProcessPredecessors(ChildEntry);
293         } else {
294           LLVM_DEBUG(errs()
295                      << "known child cycle "
296                      << Info.Context.print(BlockParent->getHeader()) << "\n");
297         }
298       } else {
299         Info.BlockMap.try_emplace(Block, NewCycle.get());
300         assert(!is_contained(NewCycle->Blocks, Block));
301         NewCycle->Blocks.insert(Block);
302         ProcessPredecessors(Block);
303         Info.BlockMapTopLevel.try_emplace(Block, NewCycle.get());
304       }
305     } while (!Worklist.empty());
306 
307     Info.TopLevelCycles.push_back(std::move(NewCycle));
308   }
309 
310   // Fix top-level cycle links and compute cycle depths.
311   for (auto *TLC : Info.toplevel_cycles()) {
312     LLVM_DEBUG(errs() << "top-level cycle: "
313                       << Info.Context.print(TLC->getHeader()) << "\n");
314 
315     TLC->ParentCycle = nullptr;
316     updateDepth(TLC);
317   }
318 }
319 
320 /// \brief Recompute depth values of \p SubTree and all descendants.
321 template <typename ContextT>
updateDepth(CycleT * SubTree)322 void GenericCycleInfoCompute<ContextT>::updateDepth(CycleT *SubTree) {
323   for (CycleT *Cycle : depth_first(SubTree))
324     Cycle->Depth = Cycle->ParentCycle ? Cycle->ParentCycle->Depth + 1 : 1;
325 }
326 
327 /// \brief Compute a DFS of basic blocks starting at the function entry.
328 ///
329 /// Fills BlockDFSInfo with start/end counters and BlockPreorder.
330 template <typename ContextT>
dfs(BlockT * EntryBlock)331 void GenericCycleInfoCompute<ContextT>::dfs(BlockT *EntryBlock) {
332   SmallVector<unsigned, 8> DFSTreeStack;
333   SmallVector<BlockT *, 8> TraverseStack;
334   unsigned Counter = 0;
335   TraverseStack.emplace_back(EntryBlock);
336 
337   do {
338     BlockT *Block = TraverseStack.back();
339     LLVM_DEBUG(errs() << "DFS visiting block: " << Info.Context.print(Block)
340                       << "\n");
341     if (!BlockDFSInfo.count(Block)) {
342       // We're visiting the block for the first time. Open its DFSInfo, add
343       // successors to the traversal stack, and remember the traversal stack
344       // depth at which the block was opened, so that we can correctly record
345       // its end time.
346       LLVM_DEBUG(errs() << "  first encountered at depth "
347                         << TraverseStack.size() << "\n");
348 
349       DFSTreeStack.emplace_back(TraverseStack.size());
350       llvm::append_range(TraverseStack, successors(Block));
351 
352       bool Added = BlockDFSInfo.try_emplace(Block, ++Counter).second;
353       (void)Added;
354       assert(Added);
355       BlockPreorder.push_back(Block);
356       LLVM_DEBUG(errs() << "  preorder number: " << Counter << "\n");
357     } else {
358       assert(!DFSTreeStack.empty());
359       if (DFSTreeStack.back() == TraverseStack.size()) {
360         LLVM_DEBUG(errs() << "  ended at " << Counter << "\n");
361         BlockDFSInfo.find(Block)->second.End = Counter;
362         DFSTreeStack.pop_back();
363       } else {
364         LLVM_DEBUG(errs() << "  already done\n");
365       }
366       TraverseStack.pop_back();
367     }
368   } while (!TraverseStack.empty());
369   assert(DFSTreeStack.empty());
370 
371   LLVM_DEBUG(
372     errs() << "Preorder:\n";
373     for (int i = 0, e = BlockPreorder.size(); i != e; ++i) {
374       errs() << "  " << Info.Context.print(BlockPreorder[i]) << ": " << i << "\n";
375     }
376   );
377 }
378 
379 /// \brief Reset the object to its initial state.
clear()380 template <typename ContextT> void GenericCycleInfo<ContextT>::clear() {
381   TopLevelCycles.clear();
382   BlockMap.clear();
383   BlockMapTopLevel.clear();
384 }
385 
386 /// \brief Compute the cycle info for a function.
387 template <typename ContextT>
compute(FunctionT & F)388 void GenericCycleInfo<ContextT>::compute(FunctionT &F) {
389   GenericCycleInfoCompute<ContextT> Compute(*this);
390   Context = ContextT(&F);
391 
392   LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName()
393                     << "\n");
394   Compute.run(&F.front());
395 
396   assert(validateTree());
397 }
398 
399 template <typename ContextT>
splitCriticalEdge(BlockT * Pred,BlockT * Succ,BlockT * NewBlock)400 void GenericCycleInfo<ContextT>::splitCriticalEdge(BlockT *Pred, BlockT *Succ,
401                                                    BlockT *NewBlock) {
402   // Edge Pred-Succ is replaced by edges Pred-NewBlock and NewBlock-Succ, all
403   // cycles that had blocks Pred and Succ also get NewBlock.
404   CycleT *Cycle = getSmallestCommonCycle(getCycle(Pred), getCycle(Succ));
405   if (!Cycle)
406     return;
407 
408   addBlockToCycle(NewBlock, Cycle);
409   assert(validateTree());
410 }
411 
412 /// \brief Find the innermost cycle containing a given block.
413 ///
414 /// \returns the innermost cycle containing \p Block or nullptr if
415 ///          it is not contained in any cycle.
416 template <typename ContextT>
417 auto GenericCycleInfo<ContextT>::getCycle(const BlockT *Block) const
418     -> CycleT * {
419   return BlockMap.lookup(Block);
420 }
421 
422 /// \brief Find the innermost cycle containing both given cycles.
423 ///
424 /// \returns the innermost cycle containing both \p A and \p B
425 ///          or nullptr if there is no such cycle.
426 template <typename ContextT>
427 auto GenericCycleInfo<ContextT>::getSmallestCommonCycle(CycleT *A,
428                                                         CycleT *B) const
429     -> CycleT * {
430   if (!A || !B)
431     return nullptr;
432 
433   // If cycles A and B have different depth replace them with parent cycle
434   // until they have the same depth.
435   while (A->getDepth() > B->getDepth())
436     A = A->getParentCycle();
437   while (B->getDepth() > A->getDepth())
438     B = B->getParentCycle();
439 
440   // Cycles A and B are at same depth but may be disjoint, replace them with
441   // parent cycles until we find cycle that contains both or we run out of
442   // parent cycles.
443   while (A != B) {
444     A = A->getParentCycle();
445     B = B->getParentCycle();
446   }
447 
448   return A;
449 }
450 
451 /// \brief get the depth for the cycle which containing a given block.
452 ///
453 /// \returns the depth for the innermost cycle containing \p Block or 0 if it is
454 ///          not contained in any cycle.
455 template <typename ContextT>
getCycleDepth(const BlockT * Block)456 unsigned GenericCycleInfo<ContextT>::getCycleDepth(const BlockT *Block) const {
457   CycleT *Cycle = getCycle(Block);
458   if (!Cycle)
459     return 0;
460   return Cycle->getDepth();
461 }
462 
463 #ifndef NDEBUG
464 /// \brief Validate the internal consistency of the cycle tree.
465 ///
466 /// Note that this does \em not check that cycles are really cycles in the CFG,
467 /// or that the right set of cycles in the CFG were found.
468 template <typename ContextT>
validateTree()469 bool GenericCycleInfo<ContextT>::validateTree() const {
470   DenseSet<BlockT *> Blocks;
471   DenseSet<BlockT *> Entries;
472 
473   auto reportError = [](const char *File, int Line, const char *Cond) {
474     errs() << File << ':' << Line
475            << ": GenericCycleInfo::validateTree: " << Cond << '\n';
476   };
477 #define check(cond)                                                            \
478   do {                                                                         \
479     if (!(cond)) {                                                             \
480       reportError(__FILE__, __LINE__, #cond);                                  \
481       return false;                                                            \
482     }                                                                          \
483   } while (false)
484 
485   for (const auto *TLC : toplevel_cycles()) {
486     for (const CycleT *Cycle : depth_first(TLC)) {
487       if (Cycle->ParentCycle)
488         check(is_contained(Cycle->ParentCycle->children(), Cycle));
489 
490       for (BlockT *Block : Cycle->Blocks) {
491         auto MapIt = BlockMap.find(Block);
492         check(MapIt != BlockMap.end());
493         check(Cycle->contains(MapIt->second));
494         check(Blocks.insert(Block).second); // duplicates in block list?
495       }
496       Blocks.clear();
497 
498       check(!Cycle->Entries.empty());
499       for (BlockT *Entry : Cycle->Entries) {
500         check(Entries.insert(Entry).second); // duplicate entry?
501         check(is_contained(Cycle->Blocks, Entry));
502       }
503       Entries.clear();
504 
505       unsigned ChildDepth = 0;
506       for (const CycleT *Child : Cycle->children()) {
507         check(Child->Depth > Cycle->Depth);
508         if (!ChildDepth) {
509           ChildDepth = Child->Depth;
510         } else {
511           check(ChildDepth == Child->Depth);
512         }
513       }
514     }
515   }
516 
517   for (const auto &Entry : BlockMap) {
518     BlockT *Block = Entry.first;
519     for (const CycleT *Cycle = Entry.second; Cycle;
520          Cycle = Cycle->ParentCycle) {
521       check(is_contained(Cycle->Blocks, Block));
522     }
523   }
524 
525 #undef check
526 
527   return true;
528 }
529 #endif
530 
531 /// \brief Print the cycle info.
532 template <typename ContextT>
print(raw_ostream & Out)533 void GenericCycleInfo<ContextT>::print(raw_ostream &Out) const {
534   for (const auto *TLC : toplevel_cycles()) {
535     for (const CycleT *Cycle : depth_first(TLC)) {
536       for (unsigned I = 0; I < Cycle->Depth; ++I)
537         Out << "    ";
538 
539       Out << Cycle->print(Context) << '\n';
540     }
541   }
542 }
543 
544 } // namespace llvm
545 
546 #undef DEBUG_TYPE
547 
548 #endif // LLVM_ADT_GENERICCYCLEIMPL_H
549