mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-13 13:45:16 +00:00
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:
parent
efdac16b38
commit
1d0244aed7
228
llvm/docs/CycleTerminology.rst
Normal file
228
llvm/docs/CycleTerminology.rst
Normal 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.
|
@ -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
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
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
BIN
llvm/docs/cycle-3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
411
llvm/include/llvm/ADT/GenericCycleImpl.h
Normal file
411
llvm/include/llvm/ADT/GenericCycleImpl.h
Normal 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
|
334
llvm/include/llvm/ADT/GenericCycleInfo.h
Normal file
334
llvm/include/llvm/ADT/GenericCycleInfo.h
Normal 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
|
74
llvm/include/llvm/ADT/GenericSSAContext.h
Normal file
74
llvm/include/llvm/ADT/GenericSSAContext.h
Normal 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
|
77
llvm/include/llvm/Analysis/CycleAnalysis.h
Normal file
77
llvm/include/llvm/Analysis/CycleAnalysis.h
Normal 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
|
31
llvm/include/llvm/CodeGen/MachineCycleAnalysis.h
Normal file
31
llvm/include/llvm/CodeGen/MachineCycleAnalysis.h
Normal 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
|
@ -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
|
||||
|
58
llvm/include/llvm/CodeGen/MachineSSAContext.h
Normal file
58
llvm/include/llvm/CodeGen/MachineSSAContext.h
Normal 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
|
56
llvm/include/llvm/IR/SSAContext.h
Normal file
56
llvm/include/llvm/IR/SSAContext.h
Normal 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
|
@ -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&);
|
||||
|
@ -35,6 +35,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
|
||||
initializeCFGOnlyPrinterLegacyPassPass(Registry);
|
||||
initializeCFLAndersAAWrapperPassPass(Registry);
|
||||
initializeCFLSteensAAWrapperPassPass(Registry);
|
||||
initializeCycleInfoWrapperPassPass(Registry);
|
||||
initializeDependenceAnalysisWrapperPassPass(Registry);
|
||||
initializeDelinearizationPass(Registry);
|
||||
initializeDemandedBitsWrapperPassPass(Registry);
|
||||
|
@ -50,6 +50,7 @@ add_llvm_component_library(LLVMAnalysis
|
||||
CostModel.cpp
|
||||
CodeMetrics.cpp
|
||||
ConstantFolding.cpp
|
||||
CycleAnalysis.cpp
|
||||
DDG.cpp
|
||||
DDGPrinter.cpp
|
||||
ConstraintSystem.cpp
|
||||
|
77
llvm/lib/Analysis/CycleAnalysis.cpp
Normal file
77
llvm/lib/Analysis/CycleAnalysis.cpp
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
113
llvm/lib/CodeGen/MachineCycleAnalysis.cpp
Normal file
113
llvm/lib/CodeGen/MachineCycleAnalysis.cpp
Normal 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;
|
||||
}
|
52
llvm/lib/CodeGen/MachineSSAContext.cpp
Normal file
52
llvm/lib/CodeGen/MachineSSAContext.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -27,6 +27,7 @@ add_llvm_component_library(LLVMCore
|
||||
Globals.cpp
|
||||
IRBuilder.cpp
|
||||
IRPrintingPasses.cpp
|
||||
SSAContext.cpp
|
||||
InlineAsm.cpp
|
||||
Instruction.cpp
|
||||
Instructions.cpp
|
||||
|
47
llvm/lib/IR/SSAContext.cpp
Normal file
47
llvm/lib/IR/SSAContext.cpp
Normal 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);
|
||||
});
|
||||
}
|
@ -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"
|
||||
|
@ -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()))
|
||||
|
302
llvm/test/Analysis/CycleInfo/basic.ll
Normal file
302
llvm/test/Analysis/CycleInfo/basic.ll
Normal 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
|
||||
}
|
629
llvm/test/CodeGen/X86/cycle-info.mir
Normal file
629
llvm/test/CodeGen/X86/cycle-info.mir
Normal 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
|
||||
|
||||
...
|
Loading…
x
Reference in New Issue
Block a user