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@amd.com>,
with recent refactoring by Sameer Sahasrabuddhe <sameer.sahasrabuddhe@amd.com>

Differential Revision: https://reviews.llvm.org/D112696
This commit is contained in:
Sameer Sahasrabuddhe 2021-12-10 14:36:43 +05:30
parent efdac16b38
commit 1d0244aed7
27 changed files with 2508 additions and 0 deletions

View File

@ -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.

View File

@ -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.

BIN
llvm/docs/cycle-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
llvm/docs/cycle-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
llvm/docs/cycle-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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&);

View File

@ -35,6 +35,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
initializeCFGOnlyPrinterLegacyPassPass(Registry);
initializeCFLAndersAAWrapperPassPass(Registry);
initializeCFLSteensAAWrapperPassPass(Registry);
initializeCycleInfoWrapperPassPass(Registry);
initializeDependenceAnalysisWrapperPassPass(Registry);
initializeDelinearizationPass(Registry);
initializeDemandedBitsWrapperPassPass(Registry);

View File

@ -50,6 +50,7 @@ add_llvm_component_library(LLVMAnalysis
CostModel.cpp
CodeMetrics.cpp
ConstantFolding.cpp
CycleAnalysis.cpp
DDG.cpp
DDGPrinter.cpp
ConstraintSystem.cpp

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}
}
});
}

View File

@ -27,6 +27,7 @@ add_llvm_component_library(LLVMCore
Globals.cpp
IRBuilder.cpp
IRPrintingPasses.cpp
SSAContext.cpp
InlineAsm.cpp
Instruction.cpp
Instructions.cpp

View File

@ -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);
});
}

View File

@ -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"

View File

@ -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()))

View File

@ -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
}

View File

@ -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
...