LLVM 17.0.0git
GenericCycleImpl.h
Go to the documentation of this file.
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"
29
30#define DEBUG_TYPE "generic-cycle-impl"
31
32namespace llvm {
33
34template <typename ContextT>
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
46template <typename ContextT>
48 SmallVectorImpl<BlockT *> &TmpStorage) const {
49 TmpStorage.clear();
50
51 size_t NumExitBlocks = 0;
52 for (BlockT *Block : blocks()) {
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
69template <typename ContextT>
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
87template <typename ContextT>
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.
108template <typename ContextT> class GenericCycleInfoCompute {
109 using BlockT = typename ContextT::BlockT;
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;
120 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.
124 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
133 GenericCycleInfoCompute &operator=(const GenericCycleInfoCompute &) = delete;
134
135public:
137
138 void run(BlockT *EntryBlock);
139
140 static void updateDepth(CycleT *SubTree);
141
142private:
143 void dfs(BlockT *EntryBlock);
144};
145
146template <typename ContextT>
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
164template <typename ContextT>
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(Child->block_begin(), Child->block_end());
181
182 for (auto &It : BlockMapTopLevel)
183 if (It.second == Child)
184 It.second = NewParent;
185}
186
187/// \brief Main function of the cycle info computations.
188template <typename ContextT>
190 LLVM_DEBUG(errs() << "Entry block: " << Info.Context.print(EntryBlock)
191 << "\n");
192 dfs(EntryBlock);
193
195
196 for (BlockT *HeaderCandidate : llvm::reverse(BlockPreorder)) {
197 const DFSInfo CandidateInfo = BlockDFSInfo.lookup(HeaderCandidate);
198
199 for (BlockT *Pred : predecessors(HeaderCandidate)) {
200 const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred);
201 if (CandidateInfo.isAncestorOf(PredDFSInfo))
202 Worklist.push_back(Pred);
203 }
204 if (Worklist.empty()) {
205 continue;
206 }
207
208 // Found a cycle with the candidate as its header.
209 LLVM_DEBUG(errs() << "Found cycle for header: "
210 << Info.Context.print(HeaderCandidate) << "\n");
211 std::unique_ptr<CycleT> NewCycle = std::make_unique<CycleT>();
212 NewCycle->appendEntry(HeaderCandidate);
213 NewCycle->appendBlock(HeaderCandidate);
214 Info.BlockMap.try_emplace(HeaderCandidate, NewCycle.get());
215
216 // Helper function to process (non-back-edge) predecessors of a discovered
217 // block and either add them to the worklist or recognize that the given
218 // block is an additional cycle entry.
219 auto ProcessPredecessors = [&](BlockT *Block) {
220 LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": ");
221
222 bool IsEntry = false;
223 for (BlockT *Pred : predecessors(Block)) {
224 const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred);
225 if (CandidateInfo.isAncestorOf(PredDFSInfo)) {
226 Worklist.push_back(Pred);
227 } else {
228 IsEntry = true;
229 }
230 }
231 if (IsEntry) {
232 assert(!NewCycle->isEntry(Block));
233 LLVM_DEBUG(errs() << "append as entry\n");
234 NewCycle->appendEntry(Block);
235 } else {
236 LLVM_DEBUG(errs() << "append as child\n");
237 }
238 };
239
240 do {
241 BlockT *Block = Worklist.pop_back_val();
242 if (Block == HeaderCandidate)
243 continue;
244
245 // If the block has already been discovered by some cycle
246 // (possibly by ourself), then the outermost cycle containing it
247 // should become our child.
248 if (auto *BlockParent = Info.getTopLevelParentCycle(Block)) {
249 LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": ");
250
251 if (BlockParent != NewCycle.get()) {
253 << "discovered child cycle "
254 << Info.Context.print(BlockParent->getHeader()) << "\n");
255 // Make BlockParent the child of NewCycle.
256 Info.moveTopLevelCycleToNewParent(NewCycle.get(), BlockParent);
258 for (auto *ChildEntry : BlockParent->entries())
259 ProcessPredecessors(ChildEntry);
260 } else {
262 << "known child cycle "
263 << Info.Context.print(BlockParent->getHeader()) << "\n");
265 } else {
266 Info.BlockMap.try_emplace(Block, NewCycle.get());
267 assert(!is_contained(NewCycle->Blocks, Block));
268 NewCycle->Blocks.insert(Block);
269 ProcessPredecessors(Block);
270 Info.BlockMapTopLevel.try_emplace(Block, NewCycle.get());
272 } while (!Worklist.empty());
273
274 Info.TopLevelCycles.push_back(std::move(NewCycle));
275 }
276
277 // Fix top-level cycle links and compute cycle depths.
278 for (auto *TLC : Info.toplevel_cycles()) {
279 LLVM_DEBUG(errs() << "top-level cycle: "
280 << Info.Context.print(TLC->getHeader()) << "\n");
281
282 TLC->ParentCycle = nullptr;
283 updateDepth(TLC);
284 }
285}
286
287/// \brief Recompute depth values of \p SubTree and all descendants.
288template <typename ContextT>
290 for (CycleT *Cycle : depth_first(SubTree))
291 Cycle->Depth = Cycle->ParentCycle ? Cycle->ParentCycle->Depth + 1 : 1;
292}
293
294/// \brief Compute a DFS of basic blocks starting at the function entry.
295///
296/// Fills BlockDFSInfo with start/end counters and BlockPreorder.
297template <typename ContextT>
298void GenericCycleInfoCompute<ContextT>::dfs(BlockT *EntryBlock) {
299 SmallVector<unsigned, 8> DFSTreeStack;
300 SmallVector<BlockT *, 8> TraverseStack;
301 unsigned Counter = 0;
302 TraverseStack.emplace_back(EntryBlock);
303
304 do {
305 BlockT *Block = TraverseStack.back();
306 LLVM_DEBUG(errs() << "DFS visiting block: " << Info.Context.print(Block)
307 << "\n");
308 if (!BlockDFSInfo.count(Block)) {
309 // We're visiting the block for the first time. Open its DFSInfo, add
310 // successors to the traversal stack, and remember the traversal stack
311 // depth at which the block was opened, so that we can correctly record
312 // its end time.
313 LLVM_DEBUG(errs() << " first encountered at depth "
314 << TraverseStack.size() << "\n");
315
316 DFSTreeStack.emplace_back(TraverseStack.size());
317 llvm::append_range(TraverseStack, successors(Block));
318
319 bool Added = BlockDFSInfo.try_emplace(Block, ++Counter).second;
320 (void)Added;
321 assert(Added);
322 BlockPreorder.push_back(Block);
323 LLVM_DEBUG(errs() << " preorder number: " << Counter << "\n");
324 } else {
325 assert(!DFSTreeStack.empty());
326 if (DFSTreeStack.back() == TraverseStack.size()) {
327 LLVM_DEBUG(errs() << " ended at " << Counter << "\n");
328 BlockDFSInfo.find(Block)->second.End = Counter;
329 DFSTreeStack.pop_back();
330 } else {
331 LLVM_DEBUG(errs() << " already done\n");
332 }
333 TraverseStack.pop_back();
334 }
335 } while (!TraverseStack.empty());
336 assert(DFSTreeStack.empty());
337
339 errs() << "Preorder:\n";
340 for (int i = 0, e = BlockPreorder.size(); i != e; ++i) {
341 errs() << " " << Info.Context.print(BlockPreorder[i]) << ": " << i << "\n";
342 }
343 );
344}
345
346/// \brief Reset the object to its initial state.
347template <typename ContextT> void GenericCycleInfo<ContextT>::clear() {
348 TopLevelCycles.clear();
349 BlockMap.clear();
350 BlockMapTopLevel.clear();
351}
352
353/// \brief Compute the cycle info for a function.
354template <typename ContextT>
357 Context.setFunction(F);
358
359 LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName()
360 << "\n");
361 Compute.run(ContextT::getEntryBlock(F));
362
363 assert(validateTree());
364}
365
366/// \brief Find the innermost cycle containing a given block.
367///
368/// \returns the innermost cycle containing \p Block or nullptr if
369/// it is not contained in any cycle.
370template <typename ContextT>
372 -> CycleT * {
373 auto MapIt = BlockMap.find(Block);
374 if (MapIt != BlockMap.end())
375 return MapIt->second;
376 return nullptr;
377}
378
379/// \brief get the depth for the cycle which containing a given block.
380///
381/// \returns the depth for the innermost cycle containing \p Block or 0 if it is
382/// not contained in any cycle.
383template <typename ContextT>
385 CycleT *Cycle = getCycle(Block);
386 if (!Cycle)
387 return 0;
388 return Cycle->getDepth();
389}
390
391#ifndef NDEBUG
392/// \brief Validate the internal consistency of the cycle tree.
393///
394/// Note that this does \em not check that cycles are really cycles in the CFG,
395/// or that the right set of cycles in the CFG were found.
396template <typename ContextT>
399 DenseSet<BlockT *> Entries;
400
401 auto reportError = [](const char *File, int Line, const char *Cond) {
402 errs() << File << ':' << Line
403 << ": GenericCycleInfo::validateTree: " << Cond << '\n';
404 };
405#define check(cond) \
406 do { \
407 if (!(cond)) { \
408 reportError(__FILE__, __LINE__, #cond); \
409 return false; \
410 } \
411 } while (false)
412
413 for (const auto *TLC : toplevel_cycles()) {
414 for (const CycleT *Cycle : depth_first(TLC)) {
415 if (Cycle->ParentCycle)
416 check(is_contained(Cycle->ParentCycle->children(), Cycle));
417
418 for (BlockT *Block : Cycle->Blocks) {
419 auto MapIt = BlockMap.find(Block);
420 check(MapIt != BlockMap.end());
421 check(Cycle->contains(MapIt->second));
422 check(Blocks.insert(Block).second); // duplicates in block list?
423 }
424 Blocks.clear();
425
426 check(!Cycle->Entries.empty());
427 for (BlockT *Entry : Cycle->Entries) {
428 check(Entries.insert(Entry).second); // duplicate entry?
429 check(is_contained(Cycle->Blocks, Entry));
430 }
431 Entries.clear();
432
433 unsigned ChildDepth = 0;
434 for (const CycleT *Child : Cycle->children()) {
435 check(Child->Depth > Cycle->Depth);
436 if (!ChildDepth) {
437 ChildDepth = Child->Depth;
438 } else {
439 check(ChildDepth == Child->Depth);
440 }
441 }
442 }
443 }
444
445 for (const auto &Entry : BlockMap) {
446 BlockT *Block = Entry.first;
447 for (const CycleT *Cycle = Entry.second; Cycle;
448 Cycle = Cycle->ParentCycle) {
449 check(is_contained(Cycle->Blocks, Block));
450 }
451 }
452
453#undef check
454
455 return true;
456}
457#endif
458
459/// \brief Print the cycle info.
460template <typename ContextT>
462 for (const auto *TLC : toplevel_cycles()) {
463 for (const CycleT *Cycle : depth_first(TLC)) {
464 for (unsigned I = 0; I < Cycle->Depth; ++I)
465 Out << " ";
466
467 Out << Cycle->print(Context) << '\n';
468 }
469 }
470}
471
472} // namespace llvm
473
474#undef DEBUG_TYPE
475
476#endif // LLVM_ADT_GENERICCYCLEIMPL_H
aarch64 promote const
SmallVector< MachineOperand, 4 > Cond
static Error reportError(StringRef Message)
Analysis containing CSE Info
Definition: CSEInfo.cpp:27
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
#define LLVM_DEBUG(X)
Definition: Debug.h:101
This file defines the DenseSet and SmallDenseSet classes.
This file builds on the ADT/GraphTraits.h file to build generic depth first graph iterator.
bool End
Definition: ELF_riscv.cpp:464
DenseMap< Block *, BlockRelaxAux > Blocks
Definition: ELF_riscv.cpp:491
#define check(cond)
Find all cycles in a control-flow graph, including irreducible loops.
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
LLVMContext & Context
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
Definition: Value.cpp:470
Implements a dense probed hash-table based set.
Definition: DenseSet.h:271
Helper class for computing cycle information.
void run(BlockT *EntryBlock)
Main function of the cycle info computations.
GenericCycleInfoCompute(CycleInfoT &Info)
static void updateDepth(CycleT *SubTree)
Recompute depth values of SubTree and all descendants.
Cycle information for a function.
typename ContextT::FunctionT FunctionT
void print(raw_ostream &Out) const
Print the cycle info.
void clear()
Reset the object to its initial state.
bool validateTree() const
Methods for debug and self-test.
GenericCycle< ContextT > CycleT
void compute(FunctionT &F)
Compute the cycle info for a function.
unsigned getCycleDepth(const BlockT *Block) const
get the depth for the cycle which containing a given block.
typename ContextT::BlockT BlockT
CycleT * getTopLevelParentCycle(BlockT *Block)
CycleT * getCycle(const BlockT *Block) const
Find the innermost cycle containing a given block.
A possibly irreducible generalization of a Loop.
Printable print(const ContextT &Ctx) const
BlockT * getCyclePreheader() const
Return the preheader block for this cycle.
void getExitBlocks(SmallVectorImpl< BlockT * > &TmpStorage) const
Return all of the successor blocks of this cycle.
BlockT * getCyclePredecessor() const
If the cycle has exactly one entry with exactly one predecessor, return it, otherwise return nullptr.
bool contains(const BlockT *Block) const
Return whether Block is contained in the cycle.
typename ContextT::BlockT BlockT
unsigned getDepth() const
iterator_range< const_child_iterator > children() const
bool empty() const
Definition: SmallVector.h:94
size_t size() const
Definition: SmallVector.h:91
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:577
reference emplace_back(ArgTypes &&... Args)
Definition: SmallVector.h:941
void resize(size_type N)
Definition: SmallVector.h:642
void push_back(const T &Elt)
Definition: SmallVector.h:416
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1200
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto successors(const MachineBasicBlock *BB)
void append_range(Container &C, Range &&R)
Wrapper function to append a range to a container.
Definition: STLExtras.h:2129
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:511
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1846
auto predecessors(const MachineBasicBlock *BB)
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition: STLExtras.h:1976
iterator_range< df_iterator< T > > depth_first(const T &G)
unsigned succ_size(const MachineBasicBlock *BB)