[llvm] 1d0244a - Reapply CycleInfo: Introduce cycles as a generalization of loops
Sameer Sahasrabuddhe via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 10 01:08:33 PST 2021
Author: Sameer Sahasrabuddhe
Date: 2021-12-10T14:36:43+05:30
New Revision: 1d0244aed78114d5bd03ec4930d7687d6e587f99
URL: https://github.com/llvm/llvm-project/commit/1d0244aed78114d5bd03ec4930d7687d6e587f99
DIFF: https://github.com/llvm/llvm-project/commit/1d0244aed78114d5bd03ec4930d7687d6e587f99.diff
LOG: Reapply CycleInfo: Introduce cycles as a generalization of loops
Reverts 02940d6d2202. Fixes breakage in the modules build.
LLVM loops cannot represent irreducible structures in the CFG. This
change introduce the concept of cycles as a generalization of loops,
along with a CycleInfo analysis that discovers a nested
hierarchy of such cycles. This is based on Havlak (1997), Nesting of
Reducible and Irreducible Loops.
The cycle analysis is implemented as a generic template and then
instatiated for LLVM IR and Machine IR. The template relies on a new
GenericSSAContext template which must be specialized when used for
each IR.
This review is a restart of an older review request:
https://reviews.llvm.org/D83094
Original implementation by Nicolai Hähnle <nicolai.haehnle at amd.com>,
with recent refactoring by Sameer Sahasrabuddhe <sameer.sahasrabuddhe at amd.com>
Differential Revision: https://reviews.llvm.org/D112696
Added:
llvm/docs/CycleTerminology.rst
llvm/docs/cycle-1.png
llvm/docs/cycle-2.png
llvm/docs/cycle-3.png
llvm/include/llvm/ADT/GenericCycleImpl.h
llvm/include/llvm/ADT/GenericCycleInfo.h
llvm/include/llvm/ADT/GenericSSAContext.h
llvm/include/llvm/Analysis/CycleAnalysis.h
llvm/include/llvm/CodeGen/MachineCycleAnalysis.h
llvm/include/llvm/CodeGen/MachineSSAContext.h
llvm/include/llvm/IR/SSAContext.h
llvm/lib/Analysis/CycleAnalysis.cpp
llvm/lib/CodeGen/MachineCycleAnalysis.cpp
llvm/lib/CodeGen/MachineSSAContext.cpp
llvm/lib/IR/SSAContext.cpp
llvm/test/Analysis/CycleInfo/basic.ll
llvm/test/CodeGen/X86/cycle-info.mir
Modified:
llvm/docs/UserGuides.rst
llvm/include/llvm/CodeGen/MachinePassRegistry.def
llvm/include/llvm/InitializePasses.h
llvm/lib/Analysis/Analysis.cpp
llvm/lib/Analysis/CMakeLists.txt
llvm/lib/CodeGen/CMakeLists.txt
llvm/lib/CodeGen/CodeGen.cpp
llvm/lib/IR/CMakeLists.txt
llvm/lib/Passes/PassBuilder.cpp
llvm/lib/Passes/PassRegistry.def
Removed:
################################################################################
diff --git a/llvm/docs/CycleTerminology.rst b/llvm/docs/CycleTerminology.rst
new file mode 100644
index 0000000000000..3d7c397f13a84
--- /dev/null
+++ b/llvm/docs/CycleTerminology.rst
@@ -0,0 +1,228 @@
+.. _cycle-terminology:
+
+======================
+LLVM Cycle Terminology
+======================
+
+.. contents::
+ :local:
+
+Cycles
+======
+
+Cycles are a generalization of LLVM :ref:`loops <loop-terminology>`,
+defined recursively as follows [HavlakCycles]_:
+
+1. In a directed graph G, an *outermost cycle* is a maximal strongly
+ connected region with at least one internal edge. (Informational
+ note --- The requirement for at least one internal edge ensures
+ that a single basic block is a cycle only if there is an edge that
+ goes back to the same basic block.)
+2. A basic block in the cycle that can be reached from the entry of
+ the function along a path that does not visit any other basic block
+ in the cycle is called an *entry* of the cycle. A cycle can have
+ multiple entries.
+3. In any depth-first search starting from the entry of the function,
+ the first node of a cycle to be visited will be one of the entries.
+ This entry is called the *header* of the cycle. (Informational note
+ --- Thus, the header of the cycle is implementation-defined.)
+4. In any depth-first search starting from the entry, set of outermost
+ cycles found in the CFG is the same. These are the *top-level
+ cycles* that do not themselves have a parent.
+5. The cycles nested inside a cycle C with header H are the outermost
+ cycles in the subgraph induced on the set of nodes (C - H). C is
+ said to be the *parent* of these cycles, and each of these cycles
+ is a *child* of C.
+
+Thus, cycles form an implementation-defined forest where each cycle C is
+the parent of any outermost cycles nested inside C. The tree closely
+follows the nesting of loops in the same function. The unique entry of
+a reducible cycle (an LLVM loop) L dominates all its other nodes, and
+is always chosen as the header of some cycle C regardless of the DFS
+tree used. This cycle C is a superset of the loop L. For an
+irreducible cycle, no one entry dominates the nodes of the cycle. One
+of the entries is chosen as header of the cycle, in an
+implementation-defined way.
+
+.. _cycle-irreducible:
+
+A cycle is *irreducible* if it has multiple entries and it is
+*reducible* otherwise.
+
+.. _cycle-parent-block:
+
+A cycle C is said to be the *parent* of a basic block B if B occurs in
+C but not in any child cycle of C. Then B is also said to be a *child*
+of cycle C.
+
+.. _cycle-sibling:
+
+A basic block or cycle X is a *sibling* of another basic block or
+cycle Y if they both have no parent or both have the same parent.
+
+Informational notes:
+
+- Non-header entry blocks of a cycle can be contained in child cycles.
+- If the CFG is reducible, the cycles are exactly the natural loops and
+ every cycle has exactly one entry block.
+- Cycles are well-nested (by definition).
+- The entry blocks of a cycle are siblings in the dominator tree.
+
+.. [HavlakCycles] Paul Havlak, "Nesting of reducible and irreducible
+ loops." ACM Transactions on Programming Languages
+ and Systems (TOPLAS) 19.4 (1997): 557-567.
+
+.. _cycle-examples:
+
+Examples of Cycles
+==================
+
+Irreducible cycle enclosing natural loops
+-----------------------------------------
+
+.. Graphviz source; the indented blocks below form a comment.
+
+ /// | |
+ /// />A] [B<\
+ /// | \ / |
+ /// ^---C---^
+ /// |
+
+ strict digraph {
+ { rank=same; A B}
+ Entry -> A
+ Entry -> B
+ A -> A
+ A -> C
+ B -> B
+ B -> C
+ C -> A
+ C -> B
+ C -> Exit
+ }
+
+.. image:: cycle-1.png
+
+The self-loops of ``A`` and ``B`` give rise to two single-block
+natural loops. A possible hierarchy of cycles is::
+
+ cycle: {A, B, C} entries: {A, B} header: A
+ - cycle: {B, C} entries: {B, C} header: C
+ - cycle: {B} entries: {B} header: B
+
+This hierarchy arises when DFS visits the blocks in the order ``A``,
+``C``, ``B`` (in preorder).
+
+Irreducible union of two natural loops
+--------------------------------------
+
+.. Graphviz source; the indented blocks below form a comment.
+
+ /// | |
+ /// A<->B
+ /// ^ ^
+ /// | |
+ /// v v
+ /// C D
+ /// | |
+
+ strict digraph {
+ { rank=same; A B}
+ { rank=same; C D}
+ Entry -> A
+ Entry -> B
+ A -> B
+ B -> A
+ A -> C
+ C -> A
+ B -> D
+ D -> B
+ C -> Exit
+ D -> Exit
+ }
+
+.. image:: cycle-2.png
+
+There are two natural loops: ``{A, C}`` and ``{B, D}``. A possible
+hierarchy of cycles is::
+
+ cycle: {A, B, C, D} entries: {A, B} header: A
+ - cycle: {B, D} entries: {B} header: B
+
+Irreducible cycle without natural loops
+---------------------------------------
+
+.. Graphviz source; the indented blocks below form a comment.
+
+ /// | |
+ /// />A B<\
+ /// | |\ /| |
+ /// | | x | |
+ /// | |/ \| |
+ /// ^-C D-^
+ /// | |
+ ///
+
+ strict digraph {
+ { rank=same; A B}
+ { rank=same; C D}
+ Entry -> A
+ Entry -> B
+ A -> C
+ A -> D
+ B -> C
+ B -> D
+ C -> A
+ D -> B
+ C -> Exit
+ D -> Exit
+ }
+
+.. image:: cycle-3.png
+
+This graph does not contain any natural loops --- the nodes ``A``,
+``B``, ``C`` and ``D`` are siblings in the dominator tree. A possible
+hierarchy of cycles is::
+
+ cycle: {A, B, C, D} entries: {A, B} header: A
+ - cycle: {B, D} entries: {B, D} header: D
+
+.. _cycle-closed-path:
+
+Closed Paths and Cycles
+=======================
+
+A *closed path* in a CFG is a connected sequence of nodes and edges in
+the CFG whose start and end points are the same.
+
+1. If a node D dominates one or more nodes in a closed path P and P
+ does not contain D, then D dominates every node in P.
+
+ **Proof:** Let U be a node in P that is dominated by D. If there
+ was a node V in P not dominated by D, then U would be reachable
+ from the function entry node via V without passing through D, which
+ contradicts the fact that D dominates U.
+
+2. If a node D dominates one or more nodes in a closed path P and P
+ does not contain D, then there exists a cycle C that contains P but
+ not D.
+
+ **Proof:** From the above property, D dominates all the nodes in P.
+ For any nesting of cycles discovered by the implementation-defined
+ DFS, consider the smallest cycle C which contains P. For the sake
+ of contradiction, assume that D is in C. Then the header H of C
+ cannot be in P, since the header of a cycle cannot be dominated by
+ any other node in the cycle. Thus, P is in the set (C-H), and there
+ must be a smaller cycle C' in C which also contains P, but that
+ contradicts how we chose C.
+
+3. If a closed path P contains nodes U1 and U2 but not their
+ dominators D1 and D2 respectively, then there exists a cycle C that
+ contains U1 and U2 but neither of D1 and D2.
+
+ **Proof:** From the above properties, each D1 and D2 separately
+ dominate every node in P. There exists a cycle C1 (respectively,
+ C2) that contains P but not D1 (respectively, D2). Either C1 and C2
+ are the same cycle, or one of them is nested inside the other.
+ Hence there is always a cycle that contains U1 and U2 but neither
+ of D1 and D2.
diff --git a/llvm/docs/UserGuides.rst b/llvm/docs/UserGuides.rst
index 29d16c0700b90..1446e3bf5553b 100644
--- a/llvm/docs/UserGuides.rst
+++ b/llvm/docs/UserGuides.rst
@@ -27,6 +27,7 @@ intermediate LLVM representation.
CommandLine
CompileCudaWithLLVM
CoverageMappingFormat
+ CycleTerminology
DebuggingJITedCode
Docker
ExtendingLLVM
@@ -137,6 +138,9 @@ Optimizations
:doc:`LoopTerminology`
A document describing Loops and associated terms as used in LLVM.
+:doc:`CycleTerminology`
+ A document describing cycles as a generalization of loops.
+
:doc:`Vectorizers`
This document describes the current status of vectorization in LLVM.
diff --git a/llvm/docs/cycle-1.png b/llvm/docs/cycle-1.png
new file mode 100644
index 0000000000000..42efda3a21ed1
Binary files /dev/null and b/llvm/docs/cycle-1.png
diff er
diff --git a/llvm/docs/cycle-2.png b/llvm/docs/cycle-2.png
new file mode 100644
index 0000000000000..5159c4811617c
Binary files /dev/null and b/llvm/docs/cycle-2.png
diff er
diff --git a/llvm/docs/cycle-3.png b/llvm/docs/cycle-3.png
new file mode 100644
index 0000000000000..2f1492e0cb60c
Binary files /dev/null and b/llvm/docs/cycle-3.png
diff er
diff --git a/llvm/include/llvm/ADT/GenericCycleImpl.h b/llvm/include/llvm/ADT/GenericCycleImpl.h
new file mode 100644
index 0000000000000..5f29236eac475
--- /dev/null
+++ b/llvm/include/llvm/ADT/GenericCycleImpl.h
@@ -0,0 +1,411 @@
+//===- GenericCycleImpl.h -------------------------------------*- C++ -*---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This template implementation resides in a separate file so that it
+// does not get injected into every .cpp file that includes the
+// generic header.
+//
+// DO NOT INCLUDE THIS FILE WHEN MERELY USING CYCLEINFO.
+//
+// This file should only be included by files that implement a
+// specialization of the relevant templates. Currently these are:
+// - CycleAnalysis.cpp
+// - MachineCycleAnalysis.cpp
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_GENERICCYCLEIMPL_H
+#define LLVM_ADT_GENERICCYCLEIMPL_H
+
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DepthFirstIterator.h"
+#include "llvm/ADT/GenericCycleInfo.h"
+
+#define DEBUG_TYPE "generic-cycle-impl"
+
+namespace llvm {
+
+template <typename ContextT>
+bool GenericCycle<ContextT>::contains(const GenericCycle *C) const {
+ if (!C)
+ return false;
+
+ if (Depth > C->Depth)
+ return false;
+ while (Depth < C->Depth)
+ C = C->ParentCycle;
+ return this == C;
+}
+
+template <typename ContextT>
+void GenericCycle<ContextT>::getExitBlocks(
+ SmallVectorImpl<BlockT *> &TmpStorage) const {
+ TmpStorage.clear();
+
+ size_t NumExitBlocks = 0;
+ for (BlockT *Block : blocks()) {
+ llvm::append_range(TmpStorage, successors(Block));
+
+ for (size_t Idx = NumExitBlocks, End = TmpStorage.size(); Idx < End;
+ ++Idx) {
+ BlockT *Succ = TmpStorage[Idx];
+ if (!contains(Succ)) {
+ auto ExitEndIt = TmpStorage.begin() + NumExitBlocks;
+ if (std::find(TmpStorage.begin(), ExitEndIt, Succ) == ExitEndIt)
+ TmpStorage[NumExitBlocks++] = Succ;
+ }
+ }
+
+ TmpStorage.resize(NumExitBlocks);
+ }
+}
+
+/// \brief Helper class for computing cycle information.
+template <typename ContextT> class GenericCycleInfoCompute {
+ using BlockT = typename ContextT::BlockT;
+ using CycleInfoT = GenericCycleInfo<ContextT>;
+ using CycleT = typename CycleInfoT::CycleT;
+
+ CycleInfoT &Info;
+
+ struct DFSInfo {
+ unsigned Start = 0; // DFS start; positive if block is found
+ unsigned End = 0; // DFS end
+
+ DFSInfo() {}
+ explicit DFSInfo(unsigned Start) : Start(Start) {}
+
+ /// Whether this node is an ancestor (or equal to) the node \p Other
+ /// in the DFS tree.
+ bool isAncestorOf(const DFSInfo &Other) const {
+ return Start <= Other.Start && Other.End <= End;
+ }
+ };
+
+ DenseMap<BlockT *, DFSInfo> BlockDFSInfo;
+ SmallVector<BlockT *, 8> BlockPreorder;
+
+ GenericCycleInfoCompute(const GenericCycleInfoCompute &) = delete;
+ GenericCycleInfoCompute &operator=(const GenericCycleInfoCompute &) = delete;
+
+public:
+ GenericCycleInfoCompute(CycleInfoT &Info) : Info(Info) {}
+
+ void run(BlockT *EntryBlock);
+
+ static void updateDepth(CycleT *SubTree);
+
+private:
+ void dfs(BlockT *EntryBlock);
+};
+
+template <typename ContextT>
+auto GenericCycleInfo<ContextT>::getTopLevelParentCycle(
+ const BlockT *Block) const -> CycleT * {
+ auto MapIt = BlockMap.find(Block);
+ if (MapIt == BlockMap.end())
+ return nullptr;
+
+ auto *C = MapIt->second;
+ while (C->ParentCycle)
+ C = C->ParentCycle;
+ return C;
+}
+
+template <typename ContextT>
+void GenericCycleInfo<ContextT>::moveToNewParent(CycleT *NewParent,
+ CycleT *Child) {
+ auto &CurrentContainer =
+ Child->ParentCycle ? Child->ParentCycle->Children : TopLevelCycles;
+ auto Pos = llvm::find_if(CurrentContainer, [=](const auto &Ptr) -> bool {
+ return Child == Ptr.get();
+ });
+ assert(Pos != CurrentContainer.end());
+ NewParent->Children.push_back(std::move(*Pos));
+ *Pos = std::move(CurrentContainer.back());
+ CurrentContainer.pop_back();
+ Child->ParentCycle = NewParent;
+}
+
+/// \brief Main function of the cycle info computations.
+template <typename ContextT>
+void GenericCycleInfoCompute<ContextT>::run(BlockT *EntryBlock) {
+ LLVM_DEBUG(errs() << "Entry block: " << Info.Context.print(EntryBlock)
+ << "\n");
+ dfs(EntryBlock);
+
+ SmallVector<BlockT *, 8> Worklist;
+
+ for (BlockT *HeaderCandidate : llvm::reverse(BlockPreorder)) {
+ const DFSInfo CandidateInfo = BlockDFSInfo.lookup(HeaderCandidate);
+
+ for (BlockT *Pred : predecessors(HeaderCandidate)) {
+ const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred);
+ if (CandidateInfo.isAncestorOf(PredDFSInfo))
+ Worklist.push_back(Pred);
+ }
+ if (Worklist.empty()) {
+ continue;
+ }
+
+ // Found a cycle with the candidate as its header.
+ LLVM_DEBUG(errs() << "Found cycle for header: "
+ << Info.Context.print(HeaderCandidate) << "\n");
+ std::unique_ptr<CycleT> NewCycle = std::make_unique<CycleT>();
+ NewCycle->appendEntry(HeaderCandidate);
+ NewCycle->appendBlock(HeaderCandidate);
+ Info.BlockMap.try_emplace(HeaderCandidate, NewCycle.get());
+
+ // Helper function to process (non-back-edge) predecessors of a discovered
+ // block and either add them to the worklist or recognize that the given
+ // block is an additional cycle entry.
+ auto ProcessPredecessors = [&](BlockT *Block) {
+ LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": ");
+
+ bool IsEntry = false;
+ for (BlockT *Pred : predecessors(Block)) {
+ const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred);
+ if (CandidateInfo.isAncestorOf(PredDFSInfo)) {
+ Worklist.push_back(Pred);
+ } else {
+ IsEntry = true;
+ }
+ }
+ if (IsEntry) {
+ assert(!NewCycle->isEntry(Block));
+ LLVM_DEBUG(errs() << "append as entry\n");
+ NewCycle->appendEntry(Block);
+ } else {
+ LLVM_DEBUG(errs() << "append as child\n");
+ }
+ };
+
+ do {
+ BlockT *Block = Worklist.pop_back_val();
+ if (Block == HeaderCandidate)
+ continue;
+
+ // If the block has already been discovered by some cycle
+ // (possibly by ourself), then the outermost cycle containing it
+ // should become our child.
+ if (auto *BlockParent = Info.getTopLevelParentCycle(Block)) {
+ LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": ");
+
+ if (BlockParent != NewCycle.get()) {
+ LLVM_DEBUG(errs()
+ << "discovered child cycle "
+ << Info.Context.print(BlockParent->getHeader()) << "\n");
+ // Make BlockParent the child of NewCycle.
+ Info.moveToNewParent(NewCycle.get(), BlockParent);
+ NewCycle->Blocks.insert(NewCycle->Blocks.end(),
+ BlockParent->block_begin(),
+ BlockParent->block_end());
+
+ for (auto *ChildEntry : BlockParent->entries())
+ ProcessPredecessors(ChildEntry);
+ } else {
+ LLVM_DEBUG(errs()
+ << "known child cycle "
+ << Info.Context.print(BlockParent->getHeader()) << "\n");
+ }
+ } else {
+ Info.BlockMap.try_emplace(Block, NewCycle.get());
+ assert(!is_contained(NewCycle->Blocks, Block));
+ NewCycle->Blocks.push_back(Block);
+ ProcessPredecessors(Block);
+ }
+ } while (!Worklist.empty());
+
+ Info.TopLevelCycles.push_back(std::move(NewCycle));
+ }
+
+ // Fix top-level cycle links and compute cycle depths.
+ for (auto *TLC : Info.toplevel_cycles()) {
+ LLVM_DEBUG(errs() << "top-level cycle: "
+ << Info.Context.print(TLC->getHeader()) << "\n");
+
+ TLC->ParentCycle = nullptr;
+ updateDepth(TLC);
+ }
+}
+
+/// \brief Recompute depth values of \p SubTree and all descendants.
+template <typename ContextT>
+void GenericCycleInfoCompute<ContextT>::updateDepth(CycleT *SubTree) {
+ for (CycleT *Cycle : depth_first(SubTree))
+ Cycle->Depth = Cycle->ParentCycle ? Cycle->ParentCycle->Depth + 1 : 1;
+}
+
+/// \brief Compute a DFS of basic blocks starting at the function entry.
+///
+/// Fills BlockDFSInfo with start/end counters and BlockPreorder.
+template <typename ContextT>
+void GenericCycleInfoCompute<ContextT>::dfs(BlockT *EntryBlock) {
+ SmallVector<unsigned, 8> DFSTreeStack;
+ SmallVector<BlockT *, 8> TraverseStack;
+ unsigned Counter = 0;
+ TraverseStack.emplace_back(EntryBlock);
+
+ do {
+ BlockT *Block = TraverseStack.back();
+ LLVM_DEBUG(errs() << "DFS visiting block: " << Info.Context.print(Block)
+ << "\n");
+ if (!BlockDFSInfo.count(Block)) {
+ // We're visiting the block for the first time. Open its DFSInfo, add
+ // successors to the traversal stack, and remember the traversal stack
+ // depth at which the block was opened, so that we can correctly record
+ // its end time.
+ LLVM_DEBUG(errs() << " first encountered at depth "
+ << TraverseStack.size() << "\n");
+
+ DFSTreeStack.emplace_back(TraverseStack.size());
+ llvm::append_range(TraverseStack, successors(Block));
+
+ LLVM_ATTRIBUTE_UNUSED
+ bool Added = BlockDFSInfo.try_emplace(Block, ++Counter).second;
+ assert(Added);
+ BlockPreorder.push_back(Block);
+ LLVM_DEBUG(errs() << " preorder number: " << Counter << "\n");
+ } else {
+ assert(!DFSTreeStack.empty());
+ if (DFSTreeStack.back() == TraverseStack.size()) {
+ LLVM_DEBUG(errs() << " ended at " << Counter << "\n");
+ BlockDFSInfo.find(Block)->second.End = Counter;
+ DFSTreeStack.pop_back();
+ } else {
+ LLVM_DEBUG(errs() << " already done\n");
+ }
+ TraverseStack.pop_back();
+ }
+ } while (!TraverseStack.empty());
+ assert(DFSTreeStack.empty());
+
+ LLVM_DEBUG(
+ errs() << "Preorder:\n";
+ for (int i = 0, e = BlockPreorder.size(); i != e; ++i) {
+ errs() << " " << Info.Context.print(BlockPreorder[i]) << ": " << i << "\n";
+ }
+ );
+}
+
+/// \brief Reset the object to its initial state.
+template <typename ContextT> void GenericCycleInfo<ContextT>::clear() {
+ TopLevelCycles.clear();
+ BlockMap.clear();
+}
+
+/// \brief Compute the cycle info for a function.
+template <typename ContextT>
+void GenericCycleInfo<ContextT>::compute(FunctionT &F) {
+ GenericCycleInfoCompute<ContextT> Compute(*this);
+ Context.setFunction(F);
+
+ LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName()
+ << "\n");
+ Compute.run(ContextT::getEntryBlock(F));
+
+ assert(validateTree());
+}
+
+/// \brief Find the innermost cycle containing a given block.
+///
+/// \returns the innermost cycle containing \p Block or nullptr if
+/// it is not contained in any cycle.
+template <typename ContextT>
+auto GenericCycleInfo<ContextT>::getCycle(const BlockT *Block) const
+ -> CycleT * {
+ auto MapIt = BlockMap.find(Block);
+ if (MapIt != BlockMap.end())
+ return MapIt->second;
+ return nullptr;
+}
+
+/// \brief Validate the internal consistency of the cycle tree.
+///
+/// Note that this does \em not check that cycles are really cycles in the CFG,
+/// or that the right set of cycles in the CFG were found.
+template <typename ContextT>
+bool GenericCycleInfo<ContextT>::validateTree() const {
+ DenseSet<BlockT *> Blocks;
+ DenseSet<BlockT *> Entries;
+
+ auto reportError = [](const char *File, int Line, const char *Cond) {
+ errs() << File << ':' << Line
+ << ": GenericCycleInfo::validateTree: " << Cond << '\n';
+ };
+#define check(cond) \
+ do { \
+ if (!(cond)) { \
+ reportError(__FILE__, __LINE__, #cond); \
+ return false; \
+ } \
+ } while (false)
+
+ for (const auto *TLC : toplevel_cycles()) {
+ for (const CycleT *Cycle : depth_first(TLC)) {
+ if (Cycle->ParentCycle)
+ check(is_contained(Cycle->ParentCycle->children(), Cycle));
+
+ for (BlockT *Block : Cycle->Blocks) {
+ auto MapIt = BlockMap.find(Block);
+ check(MapIt != BlockMap.end());
+ check(Cycle->contains(MapIt->second));
+ check(Blocks.insert(Block).second); // duplicates in block list?
+ }
+ Blocks.clear();
+
+ check(!Cycle->Entries.empty());
+ for (BlockT *Entry : Cycle->Entries) {
+ check(Entries.insert(Entry).second); // duplicate entry?
+ check(is_contained(Cycle->Blocks, Entry));
+ }
+ Entries.clear();
+
+ unsigned ChildDepth = 0;
+ for (const CycleT *Child : Cycle->children()) {
+ check(Child->Depth > Cycle->Depth);
+ if (!ChildDepth) {
+ ChildDepth = Child->Depth;
+ } else {
+ check(ChildDepth == Child->Depth);
+ }
+ }
+ }
+ }
+
+ for (const auto &Entry : BlockMap) {
+ BlockT *Block = Entry.first;
+ for (const CycleT *Cycle = Entry.second; Cycle;
+ Cycle = Cycle->ParentCycle) {
+ check(is_contained(Cycle->Blocks, Block));
+ }
+ }
+
+#undef check
+
+ return true;
+}
+
+/// \brief Print the cycle info.
+template <typename ContextT>
+void GenericCycleInfo<ContextT>::print(raw_ostream &Out) const {
+ for (const auto *TLC : toplevel_cycles()) {
+ for (const CycleT *Cycle : depth_first(TLC)) {
+ for (unsigned I = 0; I < Cycle->Depth; ++I)
+ Out << " ";
+
+ Out << Cycle->print(Context) << '\n';
+ }
+ }
+}
+
+} // namespace llvm
+
+#undef DEBUG_TYPE
+
+#endif // LLVM_ADT_GENERICCYCLEIMPL_H
diff --git a/llvm/include/llvm/ADT/GenericCycleInfo.h b/llvm/include/llvm/ADT/GenericCycleInfo.h
new file mode 100644
index 0000000000000..aad704301e43d
--- /dev/null
+++ b/llvm/include/llvm/ADT/GenericCycleInfo.h
@@ -0,0 +1,334 @@
+//===- GenericCycleInfo.h - Info for Cycles in any IR ------*- C++ -*------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// \brief Find all cycles in a control-flow graph, including irreducible loops.
+///
+/// See docs/CycleTerminology.rst for a formal definition of cycles.
+///
+/// Briefly:
+/// - A cycle is a generalization of a loop which can represent
+/// irreducible control flow.
+/// - Cycles identified in a program are implementation defined,
+/// depending on the DFS traversal chosen.
+/// - Cycles are well-nested, and form a forest with a parent-child
+/// relationship.
+/// - In any choice of DFS, every natural loop L is represented by a
+/// unique cycle C which is a superset of L.
+/// - In the absence of irreducible control flow, the cycles are
+/// exactly the natural loops in the program.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_GENERICCYCLEINFO_H
+#define LLVM_ADT_GENERICCYCLEINFO_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/GenericSSAContext.h"
+#include "llvm/ADT/GraphTraits.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/iterator.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Printable.h"
+#include "llvm/Support/raw_ostream.h"
+#include <vector>
+
+namespace llvm {
+
+template <typename ContexT> class GenericCycleInfo;
+template <typename ContexT> class GenericCycleInfoCompute;
+
+/// A possibly irreducible generalization of a \ref Loop.
+template <typename ContextT> class GenericCycle {
+public:
+ using BlockT = typename ContextT::BlockT;
+ using FunctionT = typename ContextT::FunctionT;
+ template <typename> friend class GenericCycleInfo;
+ template <typename> friend class GenericCycleInfoCompute;
+
+private:
+ /// The parent cycle. Is null for the root "cycle". Top-level cycles point
+ /// at the root.
+ GenericCycle *ParentCycle = nullptr;
+
+ /// The entry block(s) of the cycle. The header is the only entry if
+ /// this is a loop. Is empty for the root "cycle", to avoid
+ /// unnecessary memory use.
+ SmallVector<BlockT *, 1> Entries;
+
+ /// Child cycles, if any.
+ std::vector<std::unique_ptr<GenericCycle>> Children;
+
+ /// Basic blocks that are contained in the cycle, including entry blocks,
+ /// and including blocks that are part of a child cycle.
+ std::vector<BlockT *> Blocks;
+
+ /// Depth of the cycle in the tree. The root "cycle" is at depth 0.
+ ///
+ /// \note Depths are not necessarily contiguous. However, child loops always
+ /// have strictly greater depth than their parents, and sibling loops
+ /// always have the same depth.
+ unsigned Depth = 0;
+
+ void clear() {
+ Entries.clear();
+ Children.clear();
+ Blocks.clear();
+ Depth = 0;
+ ParentCycle = nullptr;
+ }
+
+ void appendEntry(BlockT *Block) { Entries.push_back(Block); }
+ void appendBlock(BlockT *Block) { Blocks.push_back(Block); }
+
+ GenericCycle(const GenericCycle &) = delete;
+ GenericCycle &operator=(const GenericCycle &) = delete;
+ GenericCycle(GenericCycle &&Rhs) = delete;
+ GenericCycle &operator=(GenericCycle &&Rhs) = delete;
+
+public:
+ GenericCycle() = default;
+
+ /// \brief Whether the cycle is a natural loop.
+ bool isReducible() const { return Entries.size() == 1; }
+
+ BlockT *getHeader() const { return Entries[0]; }
+
+ /// \brief Return whether \p Block is an entry block of the cycle.
+ bool isEntry(BlockT *Block) const { return is_contained(Entries, Block); }
+
+ /// \brief Return whether \p Block is contained in the cycle.
+ bool contains(const BlockT *Block) const {
+ return is_contained(Blocks, Block);
+ }
+
+ /// \brief Returns true iff this cycle contains \p C.
+ ///
+ /// Note: Non-strict containment check, i.e. returns true if C is the
+ /// same cycle.
+ bool contains(const GenericCycle *C) const;
+
+ const GenericCycle *getParentCycle() const { return ParentCycle; }
+ GenericCycle *getParentCycle() { return ParentCycle; }
+ unsigned getDepth() const { return Depth; }
+
+ /// Return all of the successor blocks of this cycle.
+ ///
+ /// These are the blocks _outside of the current cycle_ which are
+ /// branched to.
+ void getExitBlocks(SmallVectorImpl<BlockT *> &TmpStorage) const;
+
+ /// Iteration over child cycles.
+ //@{
+ using const_child_iterator_base =
+ typename std::vector<std::unique_ptr<GenericCycle>>::const_iterator;
+ struct const_child_iterator
+ : iterator_adaptor_base<const_child_iterator, const_child_iterator_base> {
+ using Base =
+ iterator_adaptor_base<const_child_iterator, const_child_iterator_base>;
+
+ const_child_iterator() = default;
+ explicit const_child_iterator(const_child_iterator_base I) : Base(I) {}
+
+ const const_child_iterator_base &wrapped() { return Base::wrapped(); }
+ GenericCycle *operator*() const { return Base::I->get(); }
+ };
+
+ const_child_iterator child_begin() const {
+ return const_child_iterator{Children.begin()};
+ }
+ const_child_iterator child_end() const {
+ return const_child_iterator{Children.end()};
+ }
+ size_t getNumChildren() const { return Children.size(); }
+ iterator_range<const_child_iterator> children() const {
+ return llvm::make_range(const_child_iterator{Children.begin()},
+ const_child_iterator{Children.end()});
+ }
+ //@}
+
+ /// Iteration over blocks in the cycle (including entry blocks).
+ //@{
+ using const_block_iterator = typename std::vector<BlockT *>::const_iterator;
+
+ const_block_iterator block_begin() const {
+ return const_block_iterator{Blocks.begin()};
+ }
+ const_block_iterator block_end() const {
+ return const_block_iterator{Blocks.end()};
+ }
+ size_t getNumBlocks() const { return Blocks.size(); }
+ iterator_range<const_block_iterator> blocks() const {
+ return llvm::make_range(block_begin(), block_end());
+ }
+ //@}
+
+ /// Iteration over entry blocks.
+ //@{
+ using const_entry_iterator =
+ typename SmallVectorImpl<BlockT *>::const_iterator;
+
+ size_t getNumEntries() const { return Entries.size(); }
+ iterator_range<const_entry_iterator> entries() const {
+ return llvm::make_range(Entries.begin(), Entries.end());
+ }
+
+ Printable printEntries(const ContextT &Ctx) const {
+ return Printable([this, &Ctx](raw_ostream &Out) {
+ bool First = true;
+ for (auto *Entry : Entries) {
+ if (!First)
+ Out << ' ';
+ First = false;
+ Out << Ctx.print(Entry);
+ }
+ });
+ }
+
+ Printable print(const ContextT &Ctx) const {
+ return Printable([this, &Ctx](raw_ostream &Out) {
+ Out << "depth=" << Depth << ": entries(" << printEntries(Ctx) << ')';
+
+ for (auto *Block : Blocks) {
+ if (isEntry(Block))
+ continue;
+
+ Out << ' ' << Ctx.print(Block);
+ }
+ });
+ }
+};
+
+/// \brief Cycle information for a function.
+template <typename ContextT> class GenericCycleInfo {
+public:
+ using BlockT = typename ContextT::BlockT;
+ using CycleT = GenericCycle<ContextT>;
+ using FunctionT = typename ContextT::FunctionT;
+ template <typename> friend class GenericCycle;
+ template <typename> friend class GenericCycleInfoCompute;
+
+private:
+ ContextT Context;
+
+ /// Map basic blocks to their inner-most containing loop.
+ DenseMap<BlockT *, CycleT *> BlockMap;
+
+ /// Outermost cycles discovered by any DFS.
+ ///
+ /// Note: The implementation treats the nullptr as the parent of
+ /// every top-level cycle. See \ref contains for an example.
+ std::vector<std::unique_ptr<CycleT>> TopLevelCycles;
+
+public:
+ GenericCycleInfo() = default;
+ GenericCycleInfo(GenericCycleInfo &&) = default;
+ GenericCycleInfo &operator=(GenericCycleInfo &&) = default;
+
+ void clear();
+ void compute(FunctionT &F);
+
+ FunctionT *getFunction() const { return Context.getFunction(); }
+ const ContextT &getSSAContext() const { return Context; }
+
+ CycleT *getCycle(const BlockT *Block) const;
+ CycleT *getTopLevelParentCycle(const BlockT *Block) const;
+
+ /// Move \p Child to \p NewParent by manipulating Children vectors.
+ ///
+ /// Note: This is an incomplete operation that does not update the
+ /// list of blocks in the new parent or the depth of the subtree.
+ void moveToNewParent(CycleT *NewParent, CycleT *Child);
+
+ /// Methods for debug and self-test.
+ //@{
+ bool validateTree() const;
+ void print(raw_ostream &Out) const;
+ void dump() const { print(dbgs()); }
+ //@}
+
+ /// Iteration over top-level cycles.
+ //@{
+ using const_toplevel_iterator_base =
+ typename std::vector<std::unique_ptr<CycleT>>::const_iterator;
+ struct const_toplevel_iterator
+ : iterator_adaptor_base<const_toplevel_iterator,
+ const_toplevel_iterator_base> {
+ using Base = iterator_adaptor_base<const_toplevel_iterator,
+ const_toplevel_iterator_base>;
+
+ const_toplevel_iterator() = default;
+ explicit const_toplevel_iterator(const_toplevel_iterator_base I)
+ : Base(I) {}
+
+ const const_toplevel_iterator_base &wrapped() { return Base::wrapped(); }
+ CycleT *operator*() const { return Base::I->get(); }
+ };
+
+ const_toplevel_iterator toplevel_begin() const {
+ return const_toplevel_iterator{TopLevelCycles.begin()};
+ }
+ const_toplevel_iterator toplevel_end() const {
+ return const_toplevel_iterator{TopLevelCycles.end()};
+ }
+
+ iterator_range<const_toplevel_iterator> toplevel_cycles() const {
+ return llvm::make_range(const_toplevel_iterator{TopLevelCycles.begin()},
+ const_toplevel_iterator{TopLevelCycles.end()});
+ }
+ //@}
+};
+
+/// \brief GraphTraits for iterating over a sub-tree of the CycleT tree.
+template <typename CycleRefT, typename ChildIteratorT> struct CycleGraphTraits {
+ using NodeRef = CycleRefT;
+
+ using nodes_iterator = ChildIteratorT;
+ using ChildIteratorType = nodes_iterator;
+
+ static NodeRef getEntryNode(NodeRef Graph) { return Graph; }
+
+ static ChildIteratorType child_begin(NodeRef Ref) {
+ return Ref->child_begin();
+ }
+ static ChildIteratorType child_end(NodeRef Ref) { return Ref->child_end(); }
+
+ // Not implemented:
+ // static nodes_iterator nodes_begin(GraphType *G)
+ // static nodes_iterator nodes_end (GraphType *G)
+ // nodes_iterator/begin/end - Allow iteration over all nodes in the graph
+
+ // typedef EdgeRef - Type of Edge token in the graph, which should
+ // be cheap to copy.
+ // typedef ChildEdgeIteratorType - Type used to iterate over children edges in
+ // graph, dereference to a EdgeRef.
+
+ // static ChildEdgeIteratorType child_edge_begin(NodeRef)
+ // static ChildEdgeIteratorType child_edge_end(NodeRef)
+ // Return iterators that point to the beginning and ending of the
+ // edge list for the given callgraph node.
+ //
+ // static NodeRef edge_dest(EdgeRef)
+ // Return the destination node of an edge.
+ // static unsigned size (GraphType *G)
+ // Return total number of nodes in the graph
+};
+
+template <typename BlockT>
+struct GraphTraits<const GenericCycle<BlockT> *>
+ : CycleGraphTraits<const GenericCycle<BlockT> *,
+ typename GenericCycle<BlockT>::const_child_iterator> {};
+template <typename BlockT>
+struct GraphTraits<GenericCycle<BlockT> *>
+ : CycleGraphTraits<GenericCycle<BlockT> *,
+ typename GenericCycle<BlockT>::const_child_iterator> {};
+
+} // namespace llvm
+
+#endif // LLVM_ADT_GENERICCYCLEINFO_H
diff --git a/llvm/include/llvm/ADT/GenericSSAContext.h b/llvm/include/llvm/ADT/GenericSSAContext.h
new file mode 100644
index 0000000000000..409222547d5c3
--- /dev/null
+++ b/llvm/include/llvm/ADT/GenericSSAContext.h
@@ -0,0 +1,74 @@
+//===- GenericSSAContext.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the little GenericSSAContext<X> template class
+/// that can be used to implement IR analyses as templates.
+/// Specializing these templates allows the analyses to be used over
+/// both LLVM IR and Machine IR.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_GENERICSSACONTEXT_H
+#define LLVM_ADT_GENERICSSACONTEXT_H
+
+#include "llvm/Support/Printable.h"
+
+namespace llvm {
+
+template <typename _FunctionT> class GenericSSAContext {
+public:
+ // Specializations should provide the following types that are similar to how
+ // LLVM IR is structured:
+
+ // The smallest unit of the IR is a ValueT. The SSA context uses a ValueRefT,
+ // which is a pointer to a ValueT, since Machine IR does not have the
+ // equivalent of a ValueT.
+ //
+ // using ValueRefT = ...
+
+ // An InstT is a subclass of ValueT that itself defines one or more ValueT
+ // objects.
+ //
+ // using InstT = ... must be a subclass of Value
+
+ // A BlockT is a sequence of InstT, and forms a node of the CFG. It
+ // has global methods predecessors() and successors() that return
+ // the list of incoming CFG edges and outgoing CFG edges
+ // respectively.
+ //
+ // using BlockT = ...
+
+ // A FunctionT represents a CFG along with arguments and return values. It is
+ // the smallest complete unit of code in a Module.
+ //
+ // The compiler produces an error here if this class is implicitly
+ // specialized due to an instantiation. An explicit specialization
+ // of this template needs to be added before the instantiation point
+ // indicated by the compiler.
+ using FunctionT = typename _FunctionT::invalidTemplateInstanceError;
+
+ // Every FunctionT has a unique BlockT marked as its entry.
+ //
+ // static BlockT* getEntryBlock(FunctionT &F);
+
+ // Initialize the SSA context with information about the FunctionT being
+ // processed.
+ //
+ // void setFunction(FunctionT &function);
+ // FunctionT* getFunction() const;
+
+ // Methods to print various objects.
+ //
+ // Printable print(BlockT *block) const;
+ // Printable print(InstructionT *inst) const;
+ // Printable print(ValueRefT value) const;
+};
+} // namespace llvm
+
+#endif // LLVM_ADT_GENERICSSACONTEXT_H
diff --git a/llvm/include/llvm/Analysis/CycleAnalysis.h b/llvm/include/llvm/Analysis/CycleAnalysis.h
new file mode 100644
index 0000000000000..e16b908d6a100
--- /dev/null
+++ b/llvm/include/llvm/Analysis/CycleAnalysis.h
@@ -0,0 +1,77 @@
+//===- CycleAnalysis.h - Cycle Info for LLVM IR -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file declares an analysis pass that computes CycleInfo for
+/// LLVM IR, specialized from GenericCycleInfo.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ANALYSIS_CYCLEANALYSIS_H
+#define LLVM_ANALYSIS_CYCLEANALYSIS_H
+
+#include "llvm/ADT/GenericCycleInfo.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/SSAContext.h"
+
+namespace llvm {
+extern template class GenericCycleInfo<SSAContext>;
+extern template class GenericCycle<SSAContext>;
+
+using CycleInfo = GenericCycleInfo<SSAContext>;
+using Cycle = CycleInfo::CycleT;
+
+/// Analysis pass which computes a \ref CycleInfo.
+class CycleAnalysis : public AnalysisInfoMixin<CycleAnalysis> {
+ friend AnalysisInfoMixin<CycleAnalysis>;
+ static AnalysisKey Key;
+
+public:
+ /// Provide the result typedef for this analysis pass.
+ using Result = CycleInfo;
+
+ /// Run the analysis pass over a function and produce a dominator tree.
+ CycleInfo run(Function &F, FunctionAnalysisManager &);
+
+ // TODO: verify analysis?
+};
+
+/// Printer pass for the \c DominatorTree.
+class CycleInfoPrinterPass : public PassInfoMixin<CycleInfoPrinterPass> {
+ raw_ostream &OS;
+
+public:
+ explicit CycleInfoPrinterPass(raw_ostream &OS);
+
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+/// Legacy analysis pass which computes a \ref CycleInfo.
+class CycleInfoWrapperPass : public FunctionPass {
+ Function *F = nullptr;
+ CycleInfo CI;
+
+public:
+ static char ID;
+
+ CycleInfoWrapperPass();
+
+ CycleInfo &getCycleInfo() { return CI; }
+ const CycleInfo &getCycleInfo() const { return CI; }
+
+ bool runOnFunction(Function &F) override;
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ void releaseMemory() override;
+ void print(raw_ostream &OS, const Module *M = nullptr) const override;
+
+ // TODO: verify analysis?
+};
+
+} // end namespace llvm
+
+#endif // LLVM_ANALYSIS_CYCLEANALYSIS_H
diff --git a/llvm/include/llvm/CodeGen/MachineCycleAnalysis.h b/llvm/include/llvm/CodeGen/MachineCycleAnalysis.h
new file mode 100644
index 0000000000000..d3816bbc07805
--- /dev/null
+++ b/llvm/include/llvm/CodeGen/MachineCycleAnalysis.h
@@ -0,0 +1,31 @@
+//===- MachineCycleAnalysis.h - Cycle Info for Machine IR -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the MachineCycleInfo class, which is a thin wrapper over
+// the Machine IR instance of GenericCycleInfo.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CODEGEN_MACHINECYCLEANALYSIS_H
+#define LLVM_CODEGEN_MACHINECYCLEANALYSIS_H
+
+#include "llvm/ADT/GenericCycleInfo.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineSSAContext.h"
+
+namespace llvm {
+
+extern template class GenericCycleInfo<MachineSSAContext>;
+extern template class GenericCycle<MachineSSAContext>;
+
+using MachineCycleInfo = GenericCycleInfo<MachineSSAContext>;
+using MachineCycle = MachineCycleInfo::CycleT;
+
+} // end namespace llvm
+
+#endif // LLVM_CODEGEN_MACHINECYCLEANALYSIS_H
diff --git a/llvm/include/llvm/CodeGen/MachinePassRegistry.def b/llvm/include/llvm/CodeGen/MachinePassRegistry.def
index d79303c771d63..e6763899a083b 100644
--- a/llvm/include/llvm/CodeGen/MachinePassRegistry.def
+++ b/llvm/include/llvm/CodeGen/MachinePassRegistry.def
@@ -197,4 +197,6 @@ DUMMY_MACHINE_FUNCTION_PASS("regbankselect", RegBankSelectPass, ())
DUMMY_MACHINE_FUNCTION_PASS("instruction-select", InstructionSelectPass, ())
DUMMY_MACHINE_FUNCTION_PASS("reset-machine-function", ResetMachineFunctionPass, ())
DUMMY_MACHINE_FUNCTION_PASS("machineverifier", MachineVerifierPass, ())
+DUMMY_MACHINE_FUNCTION_PASS("machine-cycles", MachineCycleInfoWrapperPass, ())
+DUMMY_MACHINE_FUNCTION_PASS("print-machine-cycles", MachineCycleInfoPrinterPass, ())
#undef DUMMY_MACHINE_FUNCTION_PASS
diff --git a/llvm/include/llvm/CodeGen/MachineSSAContext.h b/llvm/include/llvm/CodeGen/MachineSSAContext.h
new file mode 100644
index 0000000000000..6dbf321bdeaa6
--- /dev/null
+++ b/llvm/include/llvm/CodeGen/MachineSSAContext.h
@@ -0,0 +1,58 @@
+//===- MachineSSAContext.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file declares a specialization of the GenericSSAContext<X>
+/// template class for Machine IR.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CODEGEN_MACHINESSACONTEXT_H
+#define LLVM_CODEGEN_MACHINESSACONTEXT_H
+
+#include "llvm/ADT/GenericSSAContext.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/Support/Printable.h"
+
+#include <memory>
+
+namespace llvm {
+class MachineInstr;
+class MachineBasicBlock;
+class MachineFunction;
+class Register;
+template <typename, bool> class DominatorTreeBase;
+
+inline auto successors(MachineBasicBlock *BB) { return BB->successors(); }
+inline auto predecessors(MachineBasicBlock *BB) { return BB->predecessors(); }
+
+template <> class GenericSSAContext<MachineFunction> {
+ const MachineRegisterInfo *RegInfo = nullptr;
+ MachineFunction *MF;
+
+public:
+ using BlockT = MachineBasicBlock;
+ using FunctionT = MachineFunction;
+ using InstructionT = MachineInstr;
+ using ValueRefT = Register;
+ using DominatorTreeT = DominatorTreeBase<BlockT, false>;
+
+ static MachineBasicBlock *getEntryBlock(MachineFunction &F);
+
+ void setFunction(MachineFunction &Fn);
+ MachineFunction *getFunction() const { return MF; }
+
+ Printable print(MachineBasicBlock *Block) const;
+ Printable print(MachineInstr *Inst) const;
+ Printable print(Register Value) const;
+};
+
+using MachineSSAContext = GenericSSAContext<MachineFunction>;
+} // namespace llvm
+
+#endif // LLVM_CODEGEN_MACHINESSACONTEXT_H
diff --git a/llvm/include/llvm/IR/SSAContext.h b/llvm/include/llvm/IR/SSAContext.h
new file mode 100644
index 0000000000000..9d9290a2c1d78
--- /dev/null
+++ b/llvm/include/llvm/IR/SSAContext.h
@@ -0,0 +1,56 @@
+//===- SSAContext.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file declares a specialization of the GenericSSAContext<X>
+/// class template for LLVM IR.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_SSACONTEXT_H
+#define LLVM_IR_SSACONTEXT_H
+
+#include "llvm/ADT/GenericSSAContext.h"
+#include "llvm/IR/ModuleSlotTracker.h"
+#include "llvm/Support/Printable.h"
+
+#include <memory>
+
+namespace llvm {
+class BasicBlock;
+class Function;
+class Instruction;
+class Value;
+template <typename> class SmallVectorImpl;
+template <typename, bool> class DominatorTreeBase;
+
+template <> class GenericSSAContext<Function> {
+ Function *F;
+
+public:
+ using BlockT = BasicBlock;
+ using FunctionT = Function;
+ using InstructionT = Instruction;
+ using ValueRefT = Value *;
+ using DominatorTreeT = DominatorTreeBase<BlockT, false>;
+
+ static BasicBlock *getEntryBlock(Function &F);
+
+ void setFunction(Function &Fn);
+ Function *getFunction() const { return F; }
+
+ Printable print(BasicBlock *Block) const;
+ Printable print(Instruction *Inst) const;
+ Printable print(Value *Value) const;
+};
+
+using SSAContext = GenericSSAContext<Function>;
+
+} // namespace llvm
+
+#endif // LLVM_IR_SSACONTEXT_H
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index cd818d020c31d..c6e0d9a275268 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -122,6 +122,7 @@ void initializeControlHeightReductionLegacyPassPass(PassRegistry&);
void initializeCorrelatedValuePropagationPass(PassRegistry&);
void initializeCostModelAnalysisPass(PassRegistry&);
void initializeCrossDSOCFIPass(PassRegistry&);
+void initializeCycleInfoWrapperPassPass(PassRegistry &);
void initializeDAEPass(PassRegistry&);
void initializeDAHPass(PassRegistry&);
void initializeDCELegacyPassPass(PassRegistry&);
@@ -291,6 +292,8 @@ void initializeMachineBranchProbabilityInfoPass(PassRegistry&);
void initializeMachineCSEPass(PassRegistry&);
void initializeMachineCombinerPass(PassRegistry&);
void initializeMachineCopyPropagationPass(PassRegistry&);
+void initializeMachineCycleInfoPrinterPassPass(PassRegistry &);
+void initializeMachineCycleInfoWrapperPassPass(PassRegistry &);
void initializeMachineDominanceFrontierPass(PassRegistry&);
void initializeMachineDominatorTreePass(PassRegistry&);
void initializeMachineFunctionPrinterPassPass(PassRegistry&);
diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp
index db5167061509e..177f38af13d8b 100644
--- a/llvm/lib/Analysis/Analysis.cpp
+++ b/llvm/lib/Analysis/Analysis.cpp
@@ -35,6 +35,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
initializeCFGOnlyPrinterLegacyPassPass(Registry);
initializeCFLAndersAAWrapperPassPass(Registry);
initializeCFLSteensAAWrapperPassPass(Registry);
+ initializeCycleInfoWrapperPassPass(Registry);
initializeDependenceAnalysisWrapperPassPass(Registry);
initializeDelinearizationPass(Registry);
initializeDemandedBitsWrapperPassPass(Registry);
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index 9dfc619f2b135..34ca125068b19 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -50,6 +50,7 @@ add_llvm_component_library(LLVMAnalysis
CostModel.cpp
CodeMetrics.cpp
ConstantFolding.cpp
+ CycleAnalysis.cpp
DDG.cpp
DDGPrinter.cpp
ConstraintSystem.cpp
diff --git a/llvm/lib/Analysis/CycleAnalysis.cpp b/llvm/lib/Analysis/CycleAnalysis.cpp
new file mode 100644
index 0000000000000..09c7ee67e05c6
--- /dev/null
+++ b/llvm/lib/Analysis/CycleAnalysis.cpp
@@ -0,0 +1,77 @@
+//===- CycleAnalysis.cpp - Compute CycleInfo for LLVM IR ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/CycleAnalysis.h"
+#include "llvm/ADT/GenericCycleImpl.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/InitializePasses.h"
+
+using namespace llvm;
+
+template class llvm::GenericCycleInfo<SSAContext>;
+template class llvm::GenericCycle<SSAContext>;
+
+CycleInfo CycleAnalysis::run(Function &F, FunctionAnalysisManager &) {
+ CycleInfo CI;
+ CI.compute(F);
+ return CI;
+}
+
+AnalysisKey CycleAnalysis::Key;
+
+CycleInfoPrinterPass::CycleInfoPrinterPass(raw_ostream &OS) : OS(OS) {}
+
+PreservedAnalyses CycleInfoPrinterPass::run(Function &F,
+ FunctionAnalysisManager &AM) {
+ OS << "CycleInfo for function: " << F.getName() << "\n";
+ AM.getResult<CycleAnalysis>(F).print(OS);
+
+ return PreservedAnalyses::all();
+}
+
+//===----------------------------------------------------------------------===//
+// CycleInfoWrapperPass Implementation
+//===----------------------------------------------------------------------===//
+//
+// The implementation details of the wrapper pass that holds a CycleInfo
+// suitable for use with the legacy pass manager.
+//
+//===----------------------------------------------------------------------===//
+
+char CycleInfoWrapperPass::ID = 0;
+
+CycleInfoWrapperPass::CycleInfoWrapperPass() : FunctionPass(ID) {
+ initializeCycleInfoWrapperPassPass(*PassRegistry::getPassRegistry());
+}
+
+INITIALIZE_PASS_BEGIN(CycleInfoWrapperPass, "cycles", "Cycle Info Analysis",
+ true, true)
+INITIALIZE_PASS_END(CycleInfoWrapperPass, "cycles", "Cycle Info Analysis", true,
+ true)
+
+void CycleInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.setPreservesAll();
+}
+
+bool CycleInfoWrapperPass::runOnFunction(Function &Func) {
+ CI.clear();
+
+ F = &Func;
+ CI.compute(Func);
+ return false;
+}
+
+void CycleInfoWrapperPass::print(raw_ostream &OS, const Module *) const {
+ OS << "CycleInfo for function: " << F->getName() << "\n";
+ CI.print(OS);
+}
+
+void CycleInfoWrapperPass::releaseMemory() {
+ CI.clear();
+ F = nullptr;
+}
diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt
index abe2d29545682..9d26a3f316461 100644
--- a/llvm/lib/CodeGen/CMakeLists.txt
+++ b/llvm/lib/CodeGen/CMakeLists.txt
@@ -77,6 +77,7 @@ add_llvm_component_library(LLVMCodeGen
MachineCopyPropagation.cpp
MachineCSE.cpp
MachineCheckDebugify.cpp
+ MachineCycleAnalysis.cpp
MachineDebugify.cpp
MachineDominanceFrontier.cpp
MachineDominators.cpp
@@ -104,6 +105,7 @@ add_llvm_component_library(LLVMCodeGen
MachineScheduler.cpp
MachineSink.cpp
MachineSizeOpts.cpp
+ MachineSSAContext.cpp
MachineSSAUpdater.cpp
MachineStripDebug.cpp
MachineTraceMetrics.cpp
diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp
index bbdd8aab502e6..7c236a9785d8e 100644
--- a/llvm/lib/CodeGen/CodeGen.cpp
+++ b/llvm/lib/CodeGen/CodeGen.cpp
@@ -68,6 +68,8 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
initializeMachineCSEPass(Registry);
initializeMachineCombinerPass(Registry);
initializeMachineCopyPropagationPass(Registry);
+ initializeMachineCycleInfoPrinterPassPass(Registry);
+ initializeMachineCycleInfoWrapperPassPass(Registry);
initializeMachineDominatorTreePass(Registry);
initializeMachineFunctionPrinterPassPass(Registry);
initializeMachineLICMPass(Registry);
diff --git a/llvm/lib/CodeGen/MachineCycleAnalysis.cpp b/llvm/lib/CodeGen/MachineCycleAnalysis.cpp
new file mode 100644
index 0000000000000..42a5e2b7af01b
--- /dev/null
+++ b/llvm/lib/CodeGen/MachineCycleAnalysis.cpp
@@ -0,0 +1,113 @@
+//===- MachineCycleAnalysis.cpp - Compute CycleInfo for Machine IR --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/CodeGen/MachineCycleAnalysis.h"
+#include "llvm/ADT/GenericCycleImpl.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineSSAContext.h"
+#include "llvm/InitializePasses.h"
+
+using namespace llvm;
+
+template class llvm::GenericCycleInfo<llvm::MachineSSAContext>;
+template class llvm::GenericCycle<llvm::MachineSSAContext>;
+
+namespace {
+
+/// Legacy analysis pass which computes a \ref MachineCycleInfo.
+class MachineCycleInfoWrapperPass : public MachineFunctionPass {
+ MachineFunction *F = nullptr;
+ MachineCycleInfo CI;
+
+public:
+ static char ID;
+
+ MachineCycleInfoWrapperPass();
+
+ MachineCycleInfo &getCycleInfo() { return CI; }
+ const MachineCycleInfo &getCycleInfo() const { return CI; }
+
+ bool runOnMachineFunction(MachineFunction &F) override;
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ void releaseMemory() override;
+ void print(raw_ostream &OS, const Module *M = nullptr) const override;
+
+ // TODO: verify analysis
+};
+
+class MachineCycleInfoPrinterPass : public MachineFunctionPass {
+public:
+ static char ID;
+
+ MachineCycleInfoPrinterPass();
+
+ bool runOnMachineFunction(MachineFunction &F) override;
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+
+} // namespace
+
+char MachineCycleInfoWrapperPass::ID = 0;
+
+MachineCycleInfoWrapperPass::MachineCycleInfoWrapperPass()
+ : MachineFunctionPass(ID) {
+ initializeMachineCycleInfoWrapperPassPass(*PassRegistry::getPassRegistry());
+}
+
+INITIALIZE_PASS_BEGIN(MachineCycleInfoWrapperPass, "machine-cycles",
+ "Machine Cycle Info Analysis", true, true)
+INITIALIZE_PASS_END(MachineCycleInfoWrapperPass, "machine-cycles",
+ "Machine Cycle Info Analysis", true, true)
+
+void MachineCycleInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.setPreservesAll();
+ MachineFunctionPass::getAnalysisUsage(AU);
+}
+
+bool MachineCycleInfoWrapperPass::runOnMachineFunction(MachineFunction &Func) {
+ CI.clear();
+
+ F = &Func;
+ CI.compute(Func);
+ return false;
+}
+
+void MachineCycleInfoWrapperPass::print(raw_ostream &OS, const Module *) const {
+ OS << "MachineCycleInfo for function: " << F->getName() << "\n";
+ CI.print(OS);
+}
+
+void MachineCycleInfoWrapperPass::releaseMemory() {
+ CI.clear();
+ F = nullptr;
+}
+
+char MachineCycleInfoPrinterPass::ID = 0;
+
+MachineCycleInfoPrinterPass::MachineCycleInfoPrinterPass()
+ : MachineFunctionPass(ID) {
+ initializeMachineCycleInfoPrinterPassPass(*PassRegistry::getPassRegistry());
+}
+
+INITIALIZE_PASS_BEGIN(MachineCycleInfoPrinterPass, "print-machine-cycles",
+ "Print Machine Cycle Info Analysis", true, true)
+INITIALIZE_PASS_DEPENDENCY(MachineCycleInfoWrapperPass)
+INITIALIZE_PASS_END(MachineCycleInfoPrinterPass, "print-machine-cycles",
+ "Print Machine Cycle Info Analysis", true, true)
+
+void MachineCycleInfoPrinterPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.setPreservesAll();
+ AU.addRequired<MachineCycleInfoWrapperPass>();
+ MachineFunctionPass::getAnalysisUsage(AU);
+}
+
+bool MachineCycleInfoPrinterPass::runOnMachineFunction(MachineFunction &F) {
+ auto &CI = getAnalysis<MachineCycleInfoWrapperPass>();
+ CI.print(errs());
+ return false;
+}
diff --git a/llvm/lib/CodeGen/MachineSSAContext.cpp b/llvm/lib/CodeGen/MachineSSAContext.cpp
new file mode 100644
index 0000000000000..8db893535daf8
--- /dev/null
+++ b/llvm/lib/CodeGen/MachineSSAContext.cpp
@@ -0,0 +1,52 @@
+//===- MachineSSAContext.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a specialization of the GenericSSAContext<X>
+/// template class for Machine IR.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/CodeGen/MachineSSAContext.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+MachineBasicBlock *MachineSSAContext::getEntryBlock(MachineFunction &F) {
+ return &F.front();
+}
+
+void MachineSSAContext::setFunction(MachineFunction &Fn) {
+ MF = &Fn;
+ RegInfo = &MF->getRegInfo();
+}
+
+Printable MachineSSAContext::print(MachineBasicBlock *Block) const {
+ return Printable([Block](raw_ostream &Out) { Block->printName(Out); });
+}
+
+Printable MachineSSAContext::print(MachineInstr *I) const {
+ return Printable([I](raw_ostream &Out) { I->print(Out); });
+}
+
+Printable MachineSSAContext::print(Register Value) const {
+ auto *MRI = RegInfo;
+ return Printable([MRI, Value](raw_ostream &Out) {
+ Out << printReg(Value, MRI->getTargetRegisterInfo(), 0, MRI);
+
+ if (Value) {
+ // Try to print the definition.
+ if (auto *Instr = MRI->getUniqueVRegDef(Value)) {
+ Out << ": ";
+ Instr->print(Out);
+ }
+ }
+ });
+}
diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt
index 7f6e698b10f96..2c09e57c3f970 100644
--- a/llvm/lib/IR/CMakeLists.txt
+++ b/llvm/lib/IR/CMakeLists.txt
@@ -27,6 +27,7 @@ add_llvm_component_library(LLVMCore
Globals.cpp
IRBuilder.cpp
IRPrintingPasses.cpp
+ SSAContext.cpp
InlineAsm.cpp
Instruction.cpp
Instructions.cpp
diff --git a/llvm/lib/IR/SSAContext.cpp b/llvm/lib/IR/SSAContext.cpp
new file mode 100644
index 0000000000000..a96e39f328824
--- /dev/null
+++ b/llvm/lib/IR/SSAContext.cpp
@@ -0,0 +1,47 @@
+//===- SSAContext.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a specialization of the GenericSSAContext<X>
+/// template class for LLVM IR.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/SSAContext.h"
+#include "llvm/IR/Argument.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+BasicBlock *SSAContext::getEntryBlock(Function &F) {
+ return &F.getEntryBlock();
+}
+
+void SSAContext::setFunction(Function &Fn) { F = &Fn; }
+
+Printable SSAContext::print(Value *V) const {
+ return Printable([V](raw_ostream &Out) { V->print(Out); });
+}
+
+Printable SSAContext::print(Instruction *Inst) const {
+ return print(cast<Value>(Inst));
+}
+
+Printable SSAContext::print(BasicBlock *BB) const {
+ if (BB->hasName())
+ return Printable([BB](raw_ostream &Out) { Out << BB->getName(); });
+
+ return Printable([BB](raw_ostream &Out) {
+ ModuleSlotTracker MST{BB->getParent()->getParent(), false};
+ MST.incorporateFunction(*BB->getParent());
+ Out << MST.getLocalSlot(BB);
+ });
+}
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 30ff9c92adb8d..d7615ef4e9bfa 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -28,6 +28,7 @@
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Analysis/CostModel.h"
+#include "llvm/Analysis/CycleAnalysis.h"
#include "llvm/Analysis/DDG.h"
#include "llvm/Analysis/DDGPrinter.h"
#include "llvm/Analysis/Delinearization.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 8605029e3604f..3ad11acb773a8 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -185,6 +185,7 @@ FUNCTION_ANALYSIS("aa", AAManager())
FUNCTION_ANALYSIS("assumptions", AssumptionAnalysis())
FUNCTION_ANALYSIS("block-freq", BlockFrequencyAnalysis())
FUNCTION_ANALYSIS("branch-prob", BranchProbabilityAnalysis())
+FUNCTION_ANALYSIS("cycles", CycleAnalysis())
FUNCTION_ANALYSIS("domtree", DominatorTreeAnalysis())
FUNCTION_ANALYSIS("postdomtree", PostDominatorTreeAnalysis())
FUNCTION_ANALYSIS("demanded-bits", DemandedBitsAnalysis())
@@ -304,6 +305,7 @@ FUNCTION_PASS("print<assumptions>", AssumptionPrinterPass(dbgs()))
FUNCTION_PASS("print<block-freq>", BlockFrequencyPrinterPass(dbgs()))
FUNCTION_PASS("print<branch-prob>", BranchProbabilityPrinterPass(dbgs()))
FUNCTION_PASS("print<cost-model>", CostModelPrinterPass(dbgs()))
+FUNCTION_PASS("print<cycles>", CycleInfoPrinterPass(dbgs()))
FUNCTION_PASS("print<da>", DependenceAnalysisPrinterPass(dbgs()))
FUNCTION_PASS("print<divergence>", DivergenceAnalysisPrinterPass(dbgs()))
FUNCTION_PASS("print<domtree>", DominatorTreePrinterPass(dbgs()))
diff --git a/llvm/test/Analysis/CycleInfo/basic.ll b/llvm/test/Analysis/CycleInfo/basic.ll
new file mode 100644
index 0000000000000..dd6b4fa6ad251
--- /dev/null
+++ b/llvm/test/Analysis/CycleInfo/basic.ll
@@ -0,0 +1,302 @@
+; RUN: opt < %s -cycles -analyze -enable-new-pm=0 | FileCheck %s -check-prefix=CHECK
+; RUN: opt < %s -disable-output -passes='print<cycles>' 2>&1 | FileCheck %s -check-prefix=CHECK
+
+define void @empty() {
+; CHECK-LABEL: CycleInfo for function: empty
+; CHECK-NOT: depth
+
+ ret void
+}
+
+define void @simple() {
+; CHECK-LABEL: CycleInfo for function: simple
+; CHECK: depth=1: entries(loop)
+entry:
+ br label %loop
+
+loop:
+ br i1 undef, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+define void @two_latches() {
+; CHECK-LABEL: CycleInfo for function: two_latches
+; CHECK: depth=1: entries(loop) loop_next
+entry:
+ br label %loop
+
+loop:
+ br i1 undef, label %loop, label %loop_next
+
+loop_next:
+ br i1 undef, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+define void @nested_simple() {
+; CHECK-LABEL: CycleInfo for function: nested_simple
+; CHECK: depth=1: entries(outer_header) outer_latch inner
+; CHECK: depth=2: entries(inner)
+entry:
+ br label %outer_header
+
+outer_header:
+ br label %inner
+
+inner:
+ br i1 undef, label %inner, label %outer_latch
+
+outer_latch:
+ br i1 undef, label %outer_header, label %exit
+
+exit:
+ ret void
+}
+
+define void @nested_outer_latch_in_inner_loop() {
+; CHECK-LABEL: CycleInfo for function: nested_outer_latch_in_inner_loop
+; CHECK: depth=1: entries(outer_header) inner_header inner_latch
+; CHECK: depth=2: entries(inner_header) inner_latch
+entry:
+ br label %outer_header
+
+outer_header:
+ br label %inner_header
+
+inner_header:
+ br i1 undef, label %inner_latch, label %outer_header
+
+inner_latch:
+ br i1 undef, label %exit, label %inner_header
+
+exit:
+ ret void
+}
+
+define void @sibling_loops() {
+; CHECK-LABEL: CycleInfo for function: sibling_loops
+; CHECK-DAG: depth=1: entries(left)
+; CHECK-DAG: depth=1: entries(right)
+entry:
+ br i1 undef, label %left, label %right
+
+left:
+ br i1 undef, label %left, label %exit
+
+right:
+ br i1 undef, label %right, label %exit
+
+exit:
+ ret void
+}
+
+define void @serial_loops() {
+; CHECK-LABEL: CycleInfo for function: serial_loops
+; CHECK-DAG: depth=1: entries(second)
+; CHECK-DAG: depth=1: entries(first)
+entry:
+ br label %first
+
+first:
+ br i1 undef, label %first, label %second
+
+second:
+ br i1 undef, label %second, label %exit
+
+exit:
+ ret void
+}
+
+define void @nested_sibling_loops() {
+; CHECK-LABEL: CycleInfo for function: nested_sibling_loops
+; CHECK: depth=1: entries(outer_header) left right
+; CHECK-DAG: depth=2: entries(right)
+; CHECK-DAG: depth=2: entries(left)
+entry:
+ br label %outer_header
+
+outer_header:
+ br i1 undef, label %left, label %right
+
+left:
+ switch i32 undef, label %exit [ i32 0, label %left
+ i32 1, label %outer_header ]
+
+right:
+ switch i32 undef, label %outer_header [ i32 0, label %exit
+ i32 1, label %right ]
+
+exit:
+ ret void
+}
+
+define void @deeper_nest() {
+; CHECK-LABEL: CycleInfo for function: deeper_nest
+; CHECK: depth=1: entries(outer_header) outer_latch middle_header inner_header inner_latch
+; CHECK: depth=2: entries(middle_header) inner_header inner_latch
+; CHECK: depth=3: entries(inner_header) inner_latch
+entry:
+ br label %outer_header
+
+outer_header:
+ br label %middle_header
+
+middle_header:
+ br label %inner_header
+
+inner_header:
+ br i1 undef, label %middle_header, label %inner_latch
+
+inner_latch:
+ br i1 undef, label %inner_header, label %outer_latch
+
+outer_latch:
+ br i1 undef, label %outer_header, label %exit
+
+exit:
+ ret void
+}
+
+define void @irreducible_basic() {
+; CHECK-LABEL: CycleInfo for function: irreducible_basic
+; CHECK: depth=1: entries(right left)
+entry:
+ br i1 undef, label %left, label %right
+
+left:
+ br i1 undef, label %right, label %exit
+
+right:
+ br i1 undef, label %left, label %exit
+
+exit:
+ ret void
+}
+
+define void @irreducible_mess() {
+; CHECK-LABEL: CycleInfo for function: irreducible_mess
+; CHECK: depth=1: entries(B A) D C
+; CHECK: depth=2: entries(D C A)
+; CHECK: depth=3: entries(C A)
+entry:
+ br i1 undef, label %A, label %B
+
+A:
+ br i1 undef, label %C, label %D
+
+B:
+ br i1 undef, label %C, label %D
+
+C:
+ switch i32 undef, label %A [ i32 0, label %D
+ i32 1, label %exit ]
+
+D:
+ switch i32 undef, label %B [ i32 0, label %C
+ i32 1, label %exit ]
+
+exit:
+ ret void
+}
+
+define void @irreducible_into_simple_cycle() {
+; CHECK-LABEL: CycleInfo for function: irreducible_into_simple_cycle
+; CHECK: depth=1: entries(F C A) E D B
+entry:
+ switch i32 undef, label %A [ i32 0, label %C
+ i32 1, label %F ]
+
+A:
+ br label %B
+
+B:
+ br label %C
+
+C:
+ br label %D
+
+D:
+ br i1 undef, label %E, label %exit
+
+E:
+ br label %F
+
+F:
+ br i1 undef, label %A, label %exit
+
+exit:
+ ret void
+}
+
+define void @irreducible_mountain_bug() {
+; CHECK-LABEL: CycleInfo for function: irreducible_mountain_bug
+; CHECK: depth=1: entries(while.cond)
+; CHECK: depth=2: entries(cond.end61 cond.true49) while.body63 while.cond47
+; CHECK: depth=3: entries(while.body63 cond.true49) while.cond47
+entry:
+ br i1 undef, label %if.end, label %if.then
+
+if.end:
+ br i1 undef, label %if.then7, label %if.else
+
+if.then7:
+ br label %if.end16
+
+if.else:
+ br label %if.end16
+
+if.end16:
+ br i1 undef, label %while.cond.preheader, label %if.then39
+
+while.cond.preheader:
+ br label %while.cond
+
+while.cond:
+ br i1 undef, label %cond.true49, label %lor.rhs
+
+cond.true49:
+ br i1 undef, label %if.then69, label %while.body63
+
+while.body63:
+ br i1 undef, label %exit, label %while.cond47
+
+while.cond47:
+ br i1 undef, label %cond.true49, label %cond.end61
+
+cond.end61:
+ br i1 undef, label %while.body63, label %while.cond
+
+if.then69:
+ br i1 undef, label %exit, label %while.cond
+
+lor.rhs:
+ br i1 undef, label %cond.end61, label %while.end76
+
+while.end76:
+ br label %exit
+
+if.then39:
+ br i1 undef, label %exit, label %if.end.i145
+
+if.end.i145:
+ br i1 undef, label %exit, label %if.end8.i149
+
+if.end8.i149:
+ br label %exit
+
+if.then:
+ br i1 undef, label %exit, label %if.end.i
+
+if.end.i:
+ br i1 undef, label %exit, label %if.end8.i
+
+if.end8.i:
+ br label %exit
+
+exit:
+ ret void
+}
diff --git a/llvm/test/CodeGen/X86/cycle-info.mir b/llvm/test/CodeGen/X86/cycle-info.mir
new file mode 100644
index 0000000000000..358ccb2c5e731
--- /dev/null
+++ b/llvm/test/CodeGen/X86/cycle-info.mir
@@ -0,0 +1,629 @@
+# RUN: llc -mtriple=x86_64-unknown-linux-gnu -run-pass=print-machine-cycles -o - %s 2>&1 | FileCheck %s
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: empty
+name: empty
+alignment: 16
+tracksRegLiveness: true
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: simple
+# CHECK: depth=1: entries(bb.1)
+name: simple
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ JMP_1 %bb.1
+
+ bb.1:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.2
+
+ bb.2:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: two_latches
+# CHECK: depth=1: entries(bb.1) bb.2
+name: two_latches
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ JMP_1 %bb.1
+
+ bb.1:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.2
+
+ bb.2:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.3, 5, implicit $eflags
+ JMP_1 %bb.1
+
+ bb.3:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: nested_simple
+# CHECK: depth=1: entries(bb.1) bb.3 bb.2
+# CHECK: depth=2: entries(bb.2)
+name: nested_simple
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ JMP_1 %bb.1
+
+ bb.1:
+ JMP_1 %bb.2
+
+ bb.2:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.3
+
+ bb.3:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.4
+
+ bb.4:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: nested_outer_latch_in_inner_loop
+# CHECK: depth=1: entries(bb.1) bb.2 bb.3
+# CHECK: depth=2: entries(bb.2) bb.3
+name: nested_outer_latch_in_inner_loop
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ JMP_1 %bb.1
+
+ bb.1:
+ JMP_1 %bb.2
+
+ bb.2:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.3, 5, implicit $eflags
+ JMP_1 %bb.1
+
+ bb.3:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.4, 5, implicit $eflags
+ JMP_1 %bb.2
+
+ bb.4:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: sibling_loops
+# CHECK: depth=1: entries(bb.1)
+# CHECK: depth=1: entries(bb.2)
+name: sibling_loops
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+ - { id: 2, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.2
+
+ bb.1:
+ %2:gr8 = IMPLICIT_DEF
+ TEST8ri %2, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.3
+
+ bb.2:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.3
+
+ bb.3:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: serial_loops
+# CHECK: depth=1: entries(bb.2)
+# CHECK: depth=1: entries(bb.1)
+name: serial_loops
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ JMP_1 %bb.1
+
+ bb.1:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.2
+
+ bb.2:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.3
+
+ bb.3:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: nested_sibling_loops
+# CHECK: depth=1: entries(bb.1) bb.4 bb.5 bb.3 bb.2
+# CHECK: depth=2: entries(bb.4) bb.5
+# CHECK: depth=2: entries(bb.2)
+name: nested_sibling_loops
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr32 }
+ - { id: 2, class: gr8 }
+ - { id: 3, class: gr32 }
+ - { id: 4, class: gr8 }
+ - { id: 5, class: gr32 }
+ - { id: 6, class: gr8 }
+ - { id: 7, class: gr32 }
+ - { id: 8, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ JMP_1 %bb.1
+
+ bb.1:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.3
+
+ bb.2:
+ %5:gr32 = MOV32r0 implicit-def dead $eflags
+ %6:gr8 = COPY %5.sub_8bit
+ TEST8rr %6, %6, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.6
+
+ bb.6:
+ %7:gr32 = MOV32r0 implicit-def dead $eflags
+ %8:gr8 = COPY %7.sub_8bit
+ TEST8rr %8, %8, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.4
+
+ bb.3:
+ %1:gr32 = MOV32r0 implicit-def dead $eflags
+ %2:gr8 = COPY %1.sub_8bit
+ TEST8rr %2, %2, implicit-def $eflags
+ JCC_1 %bb.4, 5, implicit $eflags
+ JMP_1 %bb.5
+
+ bb.5:
+ %3:gr32 = MOV32r0 implicit-def dead $eflags
+ %4:gr8 = COPY %3.sub_8bit
+ TEST8rr %4, %4, implicit-def $eflags
+ JCC_1 %bb.3, 5, implicit $eflags
+ JMP_1 %bb.1
+
+ bb.4:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: deeper_nest
+# CHECK: depth=1: entries(bb.1) bb.5 bb.2 bb.3 bb.4
+# CHECK: depth=2: entries(bb.2) bb.3 bb.4
+# CHECK: depth=3: entries(bb.3) bb.4
+name: deeper_nest
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+ - { id: 2, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ JMP_1 %bb.1
+
+ bb.1:
+ JMP_1 %bb.2
+
+ bb.2:
+ JMP_1 %bb.3
+
+ bb.3:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.4
+
+ bb.4:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.3, 5, implicit $eflags
+ JMP_1 %bb.5
+
+ bb.5:
+ %2:gr8 = IMPLICIT_DEF
+ TEST8ri %2, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.6
+
+ bb.6:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: irreducible_basic
+# CHECK: depth=1: entries(bb.2 bb.1)
+name: irreducible_basic
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+ - { id: 2, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.2
+
+ bb.1:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.3
+
+ bb.2:
+ %2:gr8 = IMPLICIT_DEF
+ TEST8ri %2, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.3
+
+ bb.3:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: irreducible_mess
+# CHECK: depth=1: entries(bb.2 bb.1) bb.6 bb.5 bb.3 bb.4
+# CHECK: depth=2: entries(bb.5 bb.3 bb.1) bb.4
+# CHECK: depth=3: entries(bb.3 bb.1) bb.4
+name: irreducible_mess
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+ - { id: 2, class: gr32 }
+ - { id: 3, class: gr8 }
+ - { id: 4, class: gr32 }
+ - { id: 5, class: gr8 }
+ - { id: 6, class: gr32 }
+ - { id: 7, class: gr8 }
+ - { id: 8, class: gr32 }
+ - { id: 9, class: gr8 }
+ - { id: 10, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.2
+
+ bb.1:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.3, 5, implicit $eflags
+ JMP_1 %bb.4
+
+ bb.2:
+ %10:gr8 = IMPLICIT_DEF
+ TEST8ri %10, 1, implicit-def $eflags
+ JCC_1 %bb.3, 5, implicit $eflags
+ JMP_1 %bb.4
+
+ bb.3:
+ %2:gr32 = MOV32r0 implicit-def dead $eflags
+ %3:gr8 = COPY %2.sub_8bit
+ TEST8rr %3, %3, implicit-def $eflags
+ JCC_1 %bb.4, 5, implicit $eflags
+ JMP_1 %bb.6
+
+ bb.6:
+ %4:gr32 = MOV32r0 implicit-def dead $eflags
+ %5:gr8 = COPY %4.sub_8bit
+ TEST8rr %5, %5, implicit-def $eflags
+ JCC_1 %bb.5, 5, implicit $eflags
+ JMP_1 %bb.1
+
+ bb.4:
+ %6:gr32 = MOV32r0 implicit-def dead $eflags
+ %7:gr8 = COPY %6.sub_8bit
+ TEST8rr %7, %7, implicit-def $eflags
+ JCC_1 %bb.3, 5, implicit $eflags
+ JMP_1 %bb.7
+
+ bb.7:
+ successors: %bb.5, %bb.2
+
+ %8:gr32 = MOV32r0 implicit-def dead $eflags
+ %9:gr8 = COPY %8.sub_8bit
+ TEST8rr %9, %9, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.5
+
+ bb.5:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: irreducible_into_simple_cycle
+# CHECK: depth=1: entries(bb.2 bb.7 bb.4) bb.6 bb.5 bb.3
+name: irreducible_into_simple_cycle
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr32 }
+ - { id: 1, class: gr8 }
+ - { id: 2, class: gr32 }
+ - { id: 3, class: gr8 }
+ - { id: 4, class: gr8 }
+ - { id: 5, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ %0:gr32 = MOV32r0 implicit-def dead $eflags
+ %1:gr8 = COPY %0.sub_8bit
+ TEST8rr %1, %1, implicit-def $eflags
+ JCC_1 %bb.3, 5, implicit $eflags
+ JMP_1 %bb.8
+
+ bb.8:
+ %2:gr32 = MOV32r0 implicit-def dead $eflags
+ %3:gr8 = COPY %2.sub_8bit
+ TEST8rr %3, %3, implicit-def $eflags
+ JCC_1 %bb.6, 5, implicit $eflags
+ JMP_1 %bb.1
+
+ bb.1:
+ JMP_1 %bb.2
+
+ bb.2:
+ JMP_1 %bb.3
+
+ bb.3:
+ JMP_1 %bb.4
+
+ bb.4:
+ %4:gr8 = IMPLICIT_DEF
+ TEST8ri %4, 1, implicit-def $eflags
+ JCC_1 %bb.5, 5, implicit $eflags
+ JMP_1 %bb.7
+
+ bb.5:
+ JMP_1 %bb.6
+
+ bb.6:
+ %5:gr8 = IMPLICIT_DEF
+ TEST8ri %5, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.7
+
+ bb.7:
+ RET64
+
+...
+---
+# CHECK-LABEL: MachineCycleInfo for function: irreducible_mountain_bug
+# CHECK: depth=1: entries(bb.6) bb.11 bb.10 bb.8 bb.7 bb.9 bb.12
+# CHECK: depth=2: entries(bb.10 bb.7) bb.8 bb.9
+# CHECK: depth=3: entries(bb.8 bb.7) bb.9
+name: irreducible_mountain_bug
+alignment: 16
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr8 }
+ - { id: 1, class: gr8 }
+ - { id: 2, class: gr8 }
+ - { id: 3, class: gr8 }
+ - { id: 4, class: gr8 }
+ - { id: 5, class: gr8 }
+ - { id: 6, class: gr8 }
+ - { id: 7, class: gr8 }
+ - { id: 8, class: gr8 }
+ - { id: 9, class: gr8 }
+ - { id: 10, class: gr8 }
+ - { id: 11, class: gr8 }
+ - { id: 12, class: gr8 }
+ - { id: 13, class: gr8 }
+frameInfo:
+ maxAlignment: 1
+machineFunctionInfo: {}
+body: |
+ bb.0:
+ %0:gr8 = IMPLICIT_DEF
+ TEST8ri %0, 1, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit $eflags
+ JMP_1 %bb.17
+
+ bb.1:
+ %3:gr8 = IMPLICIT_DEF
+ TEST8ri %3, 1, implicit-def $eflags
+ JCC_1 %bb.2, 5, implicit $eflags
+ JMP_1 %bb.3
+
+ bb.2:
+ JMP_1 %bb.4
+
+ bb.3:
+ JMP_1 %bb.4
+
+ bb.4:
+ %4:gr8 = IMPLICIT_DEF
+ TEST8ri %4, 1, implicit-def $eflags
+ JCC_1 %bb.5, 5, implicit $eflags
+ JMP_1 %bb.14
+
+ bb.5:
+ JMP_1 %bb.6
+
+ bb.6:
+ %7:gr8 = IMPLICIT_DEF
+ TEST8ri %7, 1, implicit-def $eflags
+ JCC_1 %bb.7, 5, implicit $eflags
+ JMP_1 %bb.12
+
+ bb.7:
+ %9:gr8 = IMPLICIT_DEF
+ TEST8ri %9, 1, implicit-def $eflags
+ JCC_1 %bb.11, 5, implicit $eflags
+ JMP_1 %bb.8
+
+ bb.8:
+ %10:gr8 = IMPLICIT_DEF
+ TEST8ri %10, 1, implicit-def $eflags
+ JCC_1 %bb.20, 5, implicit $eflags
+ JMP_1 %bb.9
+
+ bb.9:
+ %11:gr8 = IMPLICIT_DEF
+ TEST8ri %11, 1, implicit-def $eflags
+ JCC_1 %bb.7, 5, implicit $eflags
+ JMP_1 %bb.10
+
+ bb.10:
+ %12:gr8 = IMPLICIT_DEF
+ TEST8ri %12, 1, implicit-def $eflags
+ JCC_1 %bb.8, 5, implicit $eflags
+ JMP_1 %bb.6
+
+ bb.11:
+ %13:gr8 = IMPLICIT_DEF
+ TEST8ri %13, 1, implicit-def $eflags
+ JCC_1 %bb.20, 5, implicit $eflags
+ JMP_1 %bb.6
+
+ bb.12:
+ %8:gr8 = IMPLICIT_DEF
+ TEST8ri %8, 1, implicit-def $eflags
+ JCC_1 %bb.10, 5, implicit $eflags
+ JMP_1 %bb.13
+
+ bb.13:
+ JMP_1 %bb.20
+
+ bb.14:
+ %5:gr8 = IMPLICIT_DEF
+ TEST8ri %5, 1, implicit-def $eflags
+ JCC_1 %bb.20, 5, implicit $eflags
+ JMP_1 %bb.15
+
+ bb.15:
+ %6:gr8 = IMPLICIT_DEF
+ TEST8ri %6, 1, implicit-def $eflags
+ JCC_1 %bb.20, 5, implicit $eflags
+ JMP_1 %bb.16
+
+ bb.16:
+ JMP_1 %bb.20
+
+ bb.17:
+ %1:gr8 = IMPLICIT_DEF
+ TEST8ri %1, 1, implicit-def $eflags
+ JCC_1 %bb.20, 5, implicit $eflags
+ JMP_1 %bb.18
+
+ bb.18:
+ %2:gr8 = IMPLICIT_DEF
+ TEST8ri %2, 1, implicit-def $eflags
+ JCC_1 %bb.20, 5, implicit $eflags
+ JMP_1 %bb.19
+
+ bb.19:
+ JMP_1 %bb.20
+
+ bb.20:
+ RET64
+
+...
More information about the llvm-commits
mailing list