[llvm] 0fe61ec - CycleInfo: Introduce cycles as a generalization of loops

Sameer Sahasrabuddhe via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 6 22:36:37 PST 2021


Author: Sameer Sahasrabuddhe
Date: 2021-12-07T12:02:34+05:30
New Revision: 0fe61ecc2cef333250a152cd90d80d0b802b27db

URL: https://github.com/llvm/llvm-project/commit/0fe61ecc2cef333250a152cd90d80d0b802b27db
DIFF: https://github.com/llvm/llvm-project/commit/0fe61ecc2cef333250a152cd90d80d0b802b27db.diff

LOG: CycleInfo: Introduce cycles as a generalization of loops

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..189e3afbc11aa
--- /dev/null
+++ b/llvm/include/llvm/ADT/GenericCycleImpl.h
@@ -0,0 +1,409 @@
+//===- 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 GenericCycleInfo<ContextT>::Compute {
+  GenericCycleInfo &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;
+
+  friend struct GraphTraits<ContractedDomSubTree>;
+
+  Compute(const Compute &) = delete;
+  Compute &operator=(const Compute &) = delete;
+
+public:
+  Compute(GenericCycleInfo &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 GenericCycleInfo<ContextT>::Compute::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 GenericCycleInfo<ContextT>::Compute::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 GenericCycleInfo<ContextT>::Compute::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) {
+  Compute 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..afe7b685e2945
--- /dev/null
+++ b/llvm/include/llvm/ADT/GenericCycleInfo.h
@@ -0,0 +1,339 @@
+//===- 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;
+
+/// 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;
+
+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;
+
+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()});
+  }
+  //@}
+
+private:
+  // Helper classes used by the cycle info computation.
+  class ContractedDomSubTree;
+  class Compute;
+
+  friend struct GraphTraits<GenericCycleInfo::ContractedDomSubTree>;
+  friend struct DenseMapInfo<ContractedDomSubTree>;
+};
+
+/// \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..c9f30dcbe48d7
--- /dev/null
+++ b/llvm/include/llvm/CodeGen/MachineCycleAnalysis.h
@@ -0,0 +1,63 @@
+//===- 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;
+
+/// 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
+};
+
+/// Legacy analysis pass which computes a \ref MachineCycleInfo.
+class MachineCycleInfoPrinterPass : public MachineFunctionPass {
+public:
+  static char ID;
+
+  MachineCycleInfoPrinterPass();
+
+  bool runOnMachineFunction(MachineFunction &F) override;
+  void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+
+} // 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 845d7dcdebd2e..29c31db2c8afc 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 9da07cb1c4850..3d880597ae010 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 59bde10ef142d..3a41886d7505e 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..2a1b456724e6a
--- /dev/null
+++ b/llvm/lib/CodeGen/MachineCycleAnalysis.cpp
@@ -0,0 +1,87 @@
+//===- 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>;
+
+//===----------------------------------------------------------------------===//
+// MachineCycleInfoWrapperPass Implementation
+//===----------------------------------------------------------------------===//
+//
+// The implementation details of the wrapper pass that holds a MachineCycleInfo
+// suitable for use with the legacy pass manager.
+//
+//===----------------------------------------------------------------------===//
+
+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 561a881bab0c5..f897faa2c22c6 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 c2032b5b8276a..c98ede7a44e55 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())
@@ -303,6 +304,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