diff --git a/llvm/docs/CycleTerminology.rst b/llvm/docs/CycleTerminology.rst new file mode 100644 --- /dev/null +++ b/llvm/docs/CycleTerminology.rst @@ -0,0 +1,111 @@ +.. _cycle-terminology: + +====================== +LLVM Cycle Terminology +====================== + +.. contents:: + :local: + +Cycles +====== + +Cycles are a generalization of LLVM :ref:`loops `, +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. The *root cycle* consists of all basic blocks of a function. Its + header is the entry block of the function. +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 tree 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 +will always be chosen as 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 will be chosen as header of the cycle, but the choice +will be arbitrary, based on the selection of DFS tree. + +.. _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 the same parent. + +Informational notes: + +- Non-header entry blocks of a cycle can be contained in child cycles. +- Since the header of an irreducible cycle is arbitrarily chosen from + its entries, some back-edges identified by the DFS may not end on + the header of any cycle. + +.. [HavlakCycles] Paul Havlak, "Nesting of reducible and irreducible + loops." ACM Transactions on Programming Languages + and Systems (TOPLAS) 19.4 (1997): 557-567. + +.. _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 both D and P. + The header H of C' cannot be in P, since that would contradict the + fact that D dominates all nodes in P. Thus, P is in the set (C'-H), + and exactly one outermost cycle C nested in C’ must contain P, + since cycles are maximal strongly connected regions. From the + definition of C’, C cannot contain D. + +3. If a closed path P contains nodes U1 and U2 but not their + dominators D1 and D2 respectively, then there exists a cycle C that + contains U1 and U2 but neither of D1 and D2. + + **Proof:** From the above properties, each D1 and D2 separately + dominate every node in P. There exists a cycle C1 (respectively, + C2) that contains P but not D1 (respectively, D2). Either C1 and C2 + are the same cycle, or one of them is nested inside the other. + Hence there is always a cycle that contains U1 and U2 but neither + of D1 and D2. diff --git a/llvm/docs/UserGuides.rst b/llvm/docs/UserGuides.rst --- a/llvm/docs/UserGuides.rst +++ b/llvm/docs/UserGuides.rst @@ -27,6 +27,7 @@ CommandLine CompileCudaWithLLVM CoverageMappingFormat + CycleTerminology DebuggingJITedCode Docker ExtendingLLVM @@ -137,6 +138,9 @@ :doc:`LoopTerminology` A document describing Loops and associated terms as used in LLVM. +:doc:`CycleTerminology` + A document describing cycles as a generalization of loops. + :doc:`Vectorizers` This document describes the current status of vectorization in LLVM. diff --git a/llvm/include/llvm/ADT/GenericCycleImpl.h b/llvm/include/llvm/ADT/GenericCycleImpl.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ADT/GenericCycleImpl.h @@ -0,0 +1,414 @@ +//===- 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 { +/// \brief Helper class for computing the machine cycle information. +template class GenericCycleInfo::Compute { + GenericCycleInfo &Info; + + struct DfsInfo { + unsigned Start = 0; // DFS start; positive if block is found + unsigned End = 0; // DFS end + + DfsInfo() {} + explicit DfsInfo(unsigned Start) : Start(Start) {} + + /// Whether this node is an ancestor (or equal to) the node \p other + /// in the DFS tree. + bool isAncestorOf(const DfsInfo &Other) const { + return Start <= Other.Start && Other.End <= End; + } + }; + + DenseMap BlockDfsInfo; + SmallVector BlockPreorder; + + friend struct GraphTraits; + + Compute(const Compute &) = delete; + Compute &operator=(const Compute &) = delete; + +public: + Compute(GenericCycleInfo &Info) : Info(Info) {} + + void run(BlockT *EntryBlock); + + static void updateDepth(CycleT *SubTree); + +private: + void dfs(BlockT *EntryBlock); +}; + +/// \brief Main function of the cycle info computations. +template +void GenericCycleInfo::Compute::run(BlockT *EntryBlock) { + assert(Info.Root->Blocks.empty()); + + LLVM_DEBUG(errs() << "Entry block: " << Info.Context.print(EntryBlock) + << "\n"); + Info.Root->appendEntry(EntryBlock); + + dfs(EntryBlock); + + SmallVector 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 Cycle = std::make_unique(Info); + Cycle->appendEntry(HeaderCandidate); + Cycle->appendBlock(HeaderCandidate); + Info.BlockMap.try_emplace(HeaderCandidate, Cycle.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(!Cycle->isEntry(Block)); + LLVM_DEBUG(errs() << "append as entry\n"); + Cycle->appendEntry(Block); + } else { + LLVM_DEBUG(errs() << "append as child\n"); + } + }; + + do { + BlockT *Block = Worklist.pop_back_val(); + if (Block == HeaderCandidate) + continue; + + auto MapIt = Info.BlockMap.find(Block); + if (MapIt != Info.BlockMap.end()) { + // The block has already been discovered by some cycle (possibly by + // ourself). Its outer-most discovered ancestor becomes our child if + // that hasn't happened yet. + LLVM_ATTRIBUTE_UNUSED + auto *BlockParent = MapIt->second; + auto *C = BlockParent; + + while (C->ParentCycle) + C = C->ParentCycle; + + LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": "); + + if (C != Cycle.get()) { + LLVM_DEBUG(errs() << "discovered child cycle " + << Info.Context.print(C->getHeader()) << "\n"); + auto RootIt = + llvm::find_if(Info.Root->Children, [=](const auto &Ptr) -> bool { + return C == Ptr.get(); + }); + assert(RootIt != Info.Root->Children.end()); + Cycle->Children.push_back(std::move(*RootIt)); + *RootIt = std::move(Info.Root->Children.back()); + Info.Root->Children.pop_back(); + + C->ParentCycle = Cycle.get(); + + Cycle->Blocks.insert(Cycle->Blocks.end(), C->Blocks.begin(), + C->Blocks.end()); + + for (BlockT *ChildEntry : C->entries()) + ProcessPredecessors(ChildEntry); + } else { + LLVM_DEBUG(errs() + << "known child cycle " + << Info.Context.print(BlockParent->getHeader()) << "\n"); + } + } else { + Info.BlockMap.try_emplace(Block, Cycle.get()); + assert(!is_contained(Cycle->Blocks, Block)); + Cycle->Blocks.push_back(Block); + ProcessPredecessors(Block); + } + } while (!Worklist.empty()); + + Info.Root->Children.push_back(std::move(Cycle)); + } + + // Fix top-level cycle links and compute cycle depths. + for (CycleT *TopLevelCycle : Info.Root->children()) { + LLVM_DEBUG(errs() << "top-level cycle: " + << Info.Context.print(TopLevelCycle->getHeader()) + << "\n"); + + TopLevelCycle->ParentCycle = Info.Root.get(); + updateDepth(TopLevelCycle); + } +} + +/// \brief Recompute depth values of \p subTree and all descendants. +template +void GenericCycleInfo::Compute::updateDepth(CycleT *SubTree) { + for (CycleT *Cycle : depth_first(SubTree)) + Cycle->Depth = Cycle->ParentCycle->Depth + 1; +} + +/// \brief Compute a DFS of basic blocks starting at the function entry. +/// +/// Fills BlockDfsInfo with start/end counters and BlockPreorder. +template +void GenericCycleInfo::Compute::dfs(BlockT *EntryBlock) { + SmallVector DfsTreeStack; + SmallVector 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"; + } + ); +} + +template +GenericCycleInfo::GenericCycleInfo() + : Root(std::make_unique(*this)) {} +template GenericCycleInfo::~GenericCycleInfo() {} + +/// \brief Reset the object to its initial state. +template void GenericCycleInfo::clear() { + Root->clear(); + BlockMap.clear(); +} + +/// \brief Compute the cycle info for a function. +template +void GenericCycleInfo::compute(FunctionT &F) { + Compute Compute(*this); + Context.incorporateFunction(F); + + LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName() + << "\n"); + Compute.run(ContextT::getEntryBlock(F)); + + assert(validateTree()); +} + +/// \brief Returns true iff \p a contains \p b. +/// +/// Note: Non-strict containment check, i.e. return true if a == b. +template +bool GenericCycleInfo::contains(const CycleT *A, + const CycleT *B) const { + if (A->Depth > B->Depth) + return false; + while (A->Depth < B->Depth) + B = B->ParentCycle; + return A == B; +} + +/// \brief Find the innermost cycle containing a given block. +/// +/// \returns the innermost cycle containing \p block or the root "cycle" if +/// is not contained in any cycle. +template +auto GenericCycleInfo::getCycle(BlockT *Block) -> CycleT * { + auto MapIt = BlockMap.find(Block); + if (MapIt != BlockMap.end()) + return MapIt->second; + return Root.get(); +} + +template +ArrayRef +GenericCycleInfo::findExitBlocks( + const CycleT *Cycle, SmallVectorImpl &TmpStorage) const { + TmpStorage.clear(); + + size_t NumExitBlocks = 0; + for (BlockT *Block : Cycle->blocks()) { + llvm::append_range(TmpStorage, successors(Block)); + + for (size_t Idx = NumExitBlocks, End = TmpStorage.size(); Idx < End; + ++Idx) { + BlockT *Succ = TmpStorage[Idx]; + if (!contains(Cycle, getCycle(Succ))) { + auto ExitEndIt = TmpStorage.begin() + NumExitBlocks; + if (std::find(TmpStorage.begin(), ExitEndIt, Succ) == ExitEndIt) + TmpStorage[NumExitBlocks++] = Succ; + } + } + + TmpStorage.resize(NumExitBlocks); + } + + return TmpStorage; +} + +/// \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 +bool GenericCycleInfo::validateTree() const { + DenseSet Blocks; + DenseSet 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 CycleT *Cycle : depth_first(getRoot())) { + if (!Cycle->ParentCycle) { + check(Cycle == getRoot()); + check(Cycle->Entries.size() == 1); + check(Cycle->Blocks.empty()); // root cycle is "empty" by convention + check(Cycle->Depth == 0); + } else { + check(Cycle != getRoot()); + check(is_contained(Cycle->ParentCycle->children(), Cycle)); + + for (BlockT *Block : Cycle->Blocks) { + auto MapIt = BlockMap.find(Block); + check(MapIt != BlockMap.end()); + check(contains(Cycle, 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(); + } + + uint 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 != getRoot(); + Cycle = Cycle->ParentCycle) { + check(is_contained(Cycle->Blocks, Block)); + } + } + +#undef check + + return true; +} + +/// \brief Print the cycle info. +template +void GenericCycleInfo::print(raw_ostream &Out) const { + for (const CycleT *Cycle : depth_first(getRoot())) { + if (!Cycle->Depth) + continue; + + for (uint I = 0; I < Cycle->Depth; ++I) + Out << " "; + + Out << Cycle->print() << '\n'; + } +} + +} // namespace llvm + +#undef DEBUG_TYPE + +#endif // LLVM_ADT_GENERICCYCLEIMPL_H diff --git a/llvm/include/llvm/ADT/GenericCycleInfo.h b/llvm/include/llvm/ADT/GenericCycleInfo.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ADT/GenericCycleInfo.h @@ -0,0 +1,402 @@ +//===- 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. +/// +/// Based on Havlak (1997), Nesting of Reducible and Irreducible Loops. +/// +/// The term "cycle" is chosen to avoid a clash with the existing notion of +/// (natural) "loop". +/// +/// Cycles are defined recursively: +/// 1. The root "cycle" consists of all basic blocks of a function. Its header +/// is the entry block of the function. +/// 2. The cycles nested inside a cycle C with header H are the maximal +/// non-trivial strongly connected components of the (induced) subgraph on +/// (C - H). (Non-trivial means that there must be an actual cycle, i.e. +/// a single basic block is only considered a cycle if it has a self-loop.) +/// +/// Every cycle has a \em header block, which is the target of a back edge in +/// the DFS tree chosen by the analysis. +/// +/// Every cycle has one or more \em entry blocks, i.e. blocks with incoming +/// arcs from outside the cycle. The header block is the first entry block; +/// there is no order among the non-header entry blocks. +/// +/// Warnings: +/// - Non-header entry blocks of a cycle can be contained in child cycles. +/// - Since the set of back edges depends on choices during a DFS (for +/// irreducible CFGs), there may be edges that "look like" back edges, but +/// whose destination block is not the header block of any cycle. +/// +/// Some interesting properties: +/// - 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. +/// - The header of every natural loop is the header of some cycle (natural +/// loop back edges are back edges in _every_ choice of DFS tree). This +/// cycle is a superset of the natural loop. +/// +/// Example (irreducible loop that contains natural loops; arcs are pointing +/// down unless indicated otherwise, brackets indicate self-loops): +/// +/// | | +/// />A] [B<\ +/// | \ / | +/// ^---C---^ +/// | +/// +/// The self-loops of A and B give rise to two single-block natural loops. +/// A possible hierarchy of cycles is: +/// - cycle of blocks A, B, C, with A as header and B as additional entry +/// block; containing: +/// -- cycle of blocks B and C, with C as header and B as additional entry +/// block; containing +/// --- singleton cycle of block B +/// This hierarchy arises when DFS visits the blocks in the order A, C, B (in +/// preorder). +/// +/// Example (irreducible loop as union of two natural loops): +/// +/// | | +/// A<->B +/// ^ ^ +/// | | +/// v v +/// C D +/// | | +/// +/// There are two natural loops: A, C and B, D. +/// A possible hierarchy of cycles is: +/// - cycle of blocks A, B, C, D, entry blocks A & B, with header A +/// -- cycle of blocks B, D, entry and header block B +/// +/// Example (irreducible loop with "back edge" not necessarily pointing to a +/// header): +/// +/// | | +/// [A<--B<\ +/// | | +/// C-----^ +/// | +/// +/// The self-loop of A gives rise to a natural loop. +/// One possible cycle analysis is the following hierarchy: +/// - cycle of blocks A, B, C, with A and B as entry blocks and B as header +/// -- singleton cycle of block A +/// Another possible cycle analysis is result is that there is a single cycle +/// of blocks A, B, C, with A and B as entry blocks and A as header. The +/// presumed back edge C->B does not target a cycle header. This analysis +/// result occurs when the DFS visits the blocks in the order A, C, B (in +/// preorder). +/// +/// Example (irreducible loop without any natural loops): +/// +/// | | +/// />A B<\ +/// | |\ /| | +/// | | x | | +/// | |/ \| | +/// ^-C D-^ +/// | | +/// +/// No natural loops, since A, B, C, D are siblings in the dominator tree. +/// A possible hierarchy of cycles is: +/// - cycle of blocks A, B, C, D, entry blocks A & B, with header A +/// -- cycle of blocks B, D, both as entry blocks, and D as header +/// +// +//===----------------------------------------------------------------------===// + +#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 + +namespace llvm { + +template class GenericCycleInfo; + +/// A possibly irreducible generalization of a \ref Loop. +template class GenericCycle { +public: + using BlockT = typename ContextT::BlockT; + using FunctionT = typename ContextT::FunctionT; + template friend class GenericCycleInfo; + +private: + const GenericCycleInfo &CI; + + /// 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 Entries; + + /// Child cycles, if any. + std::vector> Children; + + /// Basic blocks that are contained in the cycle, including entry blocks, + /// and including blocks that are part of a child cycle. + std::vector 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(const GenericCycleInfo &Info) : CI(Info) {} + + bool isRoot() const { return !ParentCycle; } + + /// \brief Whether the cycle is a natural loop. + bool isReducible() const { return !isRoot() && 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(BlockT *Block) const { return is_contained(Blocks, Block); } + + const GenericCycle *getParentCycle() const { return ParentCycle; } + GenericCycle *getParentCycle() { return ParentCycle; } + unsigned getDepth() const { return Depth; } + + /// Iteration over child cycles. + //@{ + using const_child_iterator_base = + typename std::vector>::const_iterator; + struct const_child_iterator + : iterator_adaptor_base { + using Base = + iterator_adaptor_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 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_blockref_iterator = + typename std::vector::const_iterator; + + size_t getNumBlocks() const { return Blocks.size(); } + iterator_range blocks() const { + return llvm::make_range(Blocks.begin(), Blocks.end()); + } + //@} + + /// Iteration over entry blocks. + //@{ + using const_entry_iterator = + typename SmallVectorImpl::const_iterator; + + size_t getNumEntries() const { return Entries.size(); } + iterator_range 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 { + return Printable([this](raw_ostream &Out) { + auto &Ctx = CI.Context; + 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 class GenericCycleInfo { +public: + using BlockT = typename ContextT::BlockT; + using CycleT = GenericCycle; + using FunctionT = typename ContextT::FunctionT; + +private: + ContextT Context; + + /// Root "cycle". + /// + /// A cycle structure is used here primarily to simplify the \ref + /// GraphTraits implementation and related iteration. Only the cycle + /// children and entries are filed in, while the set of blocks + /// remains empty. + /// + /// Top-level cycles link back to the root as their parent. + /// + /// A dynamic allocation is used here to allow GenericCycleInfo to be moved + /// without invalidating any cycle pointers. + std::unique_ptr Root; + + /// Map basic blocks to their inner-most containing loop. + DenseMap BlockMap; + + template friend class GenericCycle; + +public: + GenericCycleInfo(); + ~GenericCycleInfo(); + GenericCycleInfo(GenericCycleInfo &&) = default; + GenericCycleInfo &operator=(GenericCycleInfo &&) = default; + + void clear(); + void compute(FunctionT &F); + + /// \brief Return the root "cycle", which contains all the top-level cycles + /// as children. + CycleT *getRoot() { return Root.get(); } + const CycleT *getRoot() const { return Root.get(); } + + CycleT *getCycle(BlockT *Block); + const CycleT *getCycle(BlockT *Block) const { + return const_cast(this)->getCycle(Block); + } + bool contains(const CycleT *A, const CycleT *B) const; + + /// \brief Find (compute) the exit blocks of a given cycle + /// + /// Find the blocks outside the cycle with incoming edges from the cycle. + /// + /// The returned array reference remains valid as long as \p tmpStorage + /// remains unmodified. + ArrayRef + findExitBlocks(const CycleT *Cycle, + SmallVectorImpl &TmpStorage) const; + + /// Methods for debug and self-test. + //@{ + bool validateTree() const; + void print(raw_ostream &Out) const; + void dump() const { print(dbgs()); } + //@} + +private: + // Helper classes used by the cycle info computation. + class ContractedDomSubTree; + class Compute; + + friend struct GraphTraits; + friend struct DenseMapInfo; +}; + +/// \brief GraphTraits for iterating over a sub-tree of the CycleT tree. +template 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 +struct GraphTraits *> + : CycleGraphTraits *, + typename GenericCycle::const_child_iterator> {}; +template +struct GraphTraits *> + : CycleGraphTraits *, + typename GenericCycle::const_child_iterator> {}; + +} // namespace llvm + +#endif // LLVM_ADT_GENERICCYCLEINFO_H diff --git a/llvm/include/llvm/ADT/GenericSsaContext.h b/llvm/include/llvm/ADT/GenericSsaContext.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ADT/GenericSsaContext.h @@ -0,0 +1,73 @@ +//===- 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 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 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 incorporateFunction(const FunctionT &function); + + // Methods to print various objects. + // + // Printable print(BlockT *block) const; + // Printable print(InstructionT *inst) const; + // Printable print(ValueRefT value) const; +}; +} // namespace llvm + +#endif // LLVM_ADT_GENERICSSACONTEXT_H diff --git a/llvm/include/llvm/Analysis/CycleAnalysis.h b/llvm/include/llvm/Analysis/CycleAnalysis.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Analysis/CycleAnalysis.h @@ -0,0 +1,75 @@ +//===- 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; +using CycleInfo = GenericCycleInfo; +using Cycle = CycleInfo::CycleT; + +/// Analysis pass which computes a \ref CycleInfo. +class CycleAnalysis : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + 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 { + raw_ostream &OS; + +public: + explicit CycleInfoPrinterPass(raw_ostream &OS); + + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +/// Legacy analysis pass which computes a \ref CycleInfo. +class CycleInfoWrapperPass : public FunctionPass { + Function *F = nullptr; + CycleInfo CI; + +public: + static char ID; + + CycleInfoWrapperPass(); + + CycleInfo &getCycleInfo() { return CI; } + const CycleInfo &getCycleInfo() const { return CI; } + + bool runOnFunction(Function &F) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; + void releaseMemory() override; + void print(raw_ostream &OS, const Module *M = nullptr) const override; + + // TODO: verify analysis? +}; + +} // end namespace llvm + +#endif // LLVM_ANALYSIS_CYCLEANALYSIS_H diff --git a/llvm/include/llvm/CodeGen/MachineCycleAnalysis.h b/llvm/include/llvm/CodeGen/MachineCycleAnalysis.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MachineCycleAnalysis.h @@ -0,0 +1,61 @@ +//===- 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; +using MachineCycleInfo = GenericCycleInfo; +using MachineCycle = MachineCycleInfo::CycleT; + +/// Legacy analysis pass which computes a \ref MachineCycleInfo. +class MachineCycleInfoWrapperPass : public MachineFunctionPass { + MachineFunction *F = nullptr; + MachineCycleInfo CI; + +public: + static char ID; + + MachineCycleInfoWrapperPass(); + + MachineCycleInfo &getCycleInfo() { return CI; } + const MachineCycleInfo &getCycleInfo() const { return CI; } + + bool runOnMachineFunction(MachineFunction &F) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; + void releaseMemory() override; + void print(raw_ostream &OS, const Module *M = nullptr) const override; + + // TODO: verify analysis +}; + +/// Legacy analysis pass which computes a \ref MachineCycleInfo. +class MachineCycleInfoPrinterPass : public MachineFunctionPass { +public: + static char ID; + + MachineCycleInfoPrinterPass(); + + bool runOnMachineFunction(MachineFunction &F) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; + +} // end namespace llvm + +#endif // LLVM_CODEGEN_MACHINECYCLEANALYSIS_H diff --git a/llvm/include/llvm/CodeGen/MachinePassRegistry.def b/llvm/include/llvm/CodeGen/MachinePassRegistry.def --- a/llvm/include/llvm/CodeGen/MachinePassRegistry.def +++ b/llvm/include/llvm/CodeGen/MachinePassRegistry.def @@ -197,4 +197,6 @@ DUMMY_MACHINE_FUNCTION_PASS("instruction-select", InstructionSelectPass, ()) DUMMY_MACHINE_FUNCTION_PASS("reset-machine-function", ResetMachineFunctionPass, ()) DUMMY_MACHINE_FUNCTION_PASS("machineverifier", MachineVerifierPass, ()) +DUMMY_MACHINE_FUNCTION_PASS("machine-cycles", MachineCycleInfoWrapperPass, ()) +DUMMY_MACHINE_FUNCTION_PASS("print-machine-cycles", MachineCycleInfoPrinterPass, ()) #undef DUMMY_MACHINE_FUNCTION_PASS diff --git a/llvm/include/llvm/CodeGen/MachineSsaContext.h b/llvm/include/llvm/CodeGen/MachineSsaContext.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MachineSsaContext.h @@ -0,0 +1,56 @@ +//===- 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 +/// 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 + +namespace llvm { +class MachineInstr; +class MachineBasicBlock; +class MachineFunction; +class Register; +template class DominatorTreeBase; + +inline auto successors(MachineBasicBlock *BB) { return BB->successors(); } +inline auto predecessors(MachineBasicBlock *BB) { return BB->predecessors(); } + +template <> class GenericSsaContext { + const MachineRegisterInfo *RegInfo = nullptr; + +public: + using BlockT = MachineBasicBlock; + using FunctionT = MachineFunction; + using InstructionT = MachineInstr; + using ValueRefT = Register; + using DominatorTreeT = DominatorTreeBase; + + static MachineBasicBlock *getEntryBlock(MachineFunction &F); + + void incorporateFunction(const MachineFunction &Function); + + Printable print(MachineBasicBlock *Block) const; + Printable print(MachineInstr *Inst) const; + Printable print(Register Value) const; +}; + +using MachineSsaContext = GenericSsaContext; +} // namespace llvm + +#endif // LLVM_CODEGEN_MACHINESSACONTEXT_H diff --git a/llvm/include/llvm/IR/SsaContext.h b/llvm/include/llvm/IR/SsaContext.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/IR/SsaContext.h @@ -0,0 +1,53 @@ +//===- 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 +/// 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 + +namespace llvm { +class BasicBlock; +class Function; +class Instruction; +class Value; +template class SmallVectorImpl; +template class DominatorTreeBase; + +template <> class GenericSsaContext { +public: + using BlockT = BasicBlock; + using FunctionT = Function; + using InstructionT = Instruction; + using ValueRefT = Value *; + using DominatorTreeT = DominatorTreeBase; + + static BasicBlock *getEntryBlock(Function &F); + + void incorporateFunction(const Function &Function){}; + + Printable print(BasicBlock *Block) const; + Printable print(Instruction *Inst) const; + Printable print(Value *Value) const; +}; + +using SsaContext = GenericSsaContext; + +} // namespace llvm + +#endif // LLVM_IR_SSACONTEXT_H diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -119,9 +119,12 @@ void initializeConstantMergeLegacyPassPass(PassRegistry&); void initializeConstraintEliminationPass(PassRegistry &); void initializeControlHeightReductionLegacyPassPass(PassRegistry&); +void initializeConvergenceControlHeuristicLegacyPassPass(PassRegistry &); +void initializeConvergenceInfoWrapperPassPass(PassRegistry &); void initializeCorrelatedValuePropagationPass(PassRegistry&); void initializeCostModelAnalysisPass(PassRegistry&); void initializeCrossDSOCFIPass(PassRegistry&); +void initializeCycleInfoWrapperPassPass(PassRegistry &); void initializeDAEPass(PassRegistry&); void initializeDAHPass(PassRegistry&); void initializeDCELegacyPassPass(PassRegistry&); @@ -290,6 +293,8 @@ void initializeMachineCSEPass(PassRegistry&); void initializeMachineCombinerPass(PassRegistry&); void initializeMachineCopyPropagationPass(PassRegistry&); +void initializeMachineCycleInfoPrinterPassPass(PassRegistry &); +void initializeMachineCycleInfoWrapperPassPass(PassRegistry &); void initializeMachineDominanceFrontierPass(PassRegistry&); void initializeMachineDominatorTreePass(PassRegistry&); void initializeMachineFunctionPrinterPassPass(PassRegistry&); @@ -441,6 +446,7 @@ void initializeTwoAddressInstructionPassPass(PassRegistry&); void initializeTypeBasedAAWrapperPassPass(PassRegistry&); void initializeTypePromotionPass(PassRegistry&); +void initializeUniformityInfoWrapperPassPass(PassRegistry &); void initializeUnifyFunctionExitNodesLegacyPassPass(PassRegistry &); void initializeUnifyLoopExitsLegacyPassPass(PassRegistry &); void initializeUnpackMachineBundlesPass(PassRegistry&); diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp --- a/llvm/lib/Analysis/Analysis.cpp +++ b/llvm/lib/Analysis/Analysis.cpp @@ -35,6 +35,7 @@ initializeCFGOnlyPrinterLegacyPassPass(Registry); initializeCFLAndersAAWrapperPassPass(Registry); initializeCFLSteensAAWrapperPassPass(Registry); + initializeCycleInfoWrapperPassPass(Registry); initializeDependenceAnalysisWrapperPassPass(Registry); initializeDelinearizationPass(Registry); initializeDemandedBitsWrapperPassPass(Registry); diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -50,6 +50,7 @@ CostModel.cpp CodeMetrics.cpp ConstantFolding.cpp + CycleAnalysis.cpp DDG.cpp DDGPrinter.cpp ConstraintSystem.cpp diff --git a/llvm/lib/Analysis/CycleAnalysis.cpp b/llvm/lib/Analysis/CycleAnalysis.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Analysis/CycleAnalysis.cpp @@ -0,0 +1,76 @@ +//===- 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; + +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(F).print(OS); + + return PreservedAnalyses::all(); +} + +//===----------------------------------------------------------------------===// +// CycleInfoWrapperPass Implementation +//===----------------------------------------------------------------------===// +// +// The implementation details of the wrapper pass that holds a CycleInfo +// suitable for use with the legacy pass manager. +// +//===----------------------------------------------------------------------===// + +char CycleInfoWrapperPass::ID = 0; + +CycleInfoWrapperPass::CycleInfoWrapperPass() : FunctionPass(ID) { + initializeCycleInfoWrapperPassPass(*PassRegistry::getPassRegistry()); +} + +INITIALIZE_PASS_BEGIN(CycleInfoWrapperPass, "cycles", "Cycle Info Analysis", + true, true) +INITIALIZE_PASS_END(CycleInfoWrapperPass, "cycles", "Cycle Info Analysis", true, + true) + +void CycleInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); +} + +bool CycleInfoWrapperPass::runOnFunction(Function &Func) { + CI.clear(); + + F = &Func; + CI.compute(Func); + return false; +} + +void CycleInfoWrapperPass::print(raw_ostream &OS, const Module *) const { + OS << "CycleInfo for function: " << F->getName() << "\n"; + CI.print(OS); +} + +void CycleInfoWrapperPass::releaseMemory() { + CI.clear(); + F = nullptr; +} diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -77,6 +77,7 @@ MachineCopyPropagation.cpp MachineCSE.cpp MachineCheckDebugify.cpp + MachineCycleAnalysis.cpp MachineDebugify.cpp MachineDominanceFrontier.cpp MachineDominators.cpp @@ -104,6 +105,7 @@ MachineScheduler.cpp MachineSink.cpp MachineSizeOpts.cpp + MachineSsaContext.cpp MachineSSAUpdater.cpp MachineStripDebug.cpp MachineTraceMetrics.cpp diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp --- a/llvm/lib/CodeGen/CodeGen.cpp +++ b/llvm/lib/CodeGen/CodeGen.cpp @@ -66,6 +66,8 @@ initializeMachineCSEPass(Registry); initializeMachineCombinerPass(Registry); initializeMachineCopyPropagationPass(Registry); + initializeMachineCycleInfoPrinterPassPass(Registry); + initializeMachineCycleInfoWrapperPassPass(Registry); initializeMachineDominatorTreePass(Registry); initializeMachineFunctionPrinterPassPass(Registry); initializeMachineLICMPass(Registry); diff --git a/llvm/lib/CodeGen/MachineCycleAnalysis.cpp b/llvm/lib/CodeGen/MachineCycleAnalysis.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/CodeGen/MachineCycleAnalysis.cpp @@ -0,0 +1,86 @@ +//===- 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; + +//===----------------------------------------------------------------------===// +// MachineCycleInfoWrapperPass Implementation +//===----------------------------------------------------------------------===// +// +// The implementation details of the wrapper pass that holds a MachineCycleInfo +// suitable for use with the legacy pass manager. +// +//===----------------------------------------------------------------------===// + +char MachineCycleInfoWrapperPass::ID = 0; + +MachineCycleInfoWrapperPass::MachineCycleInfoWrapperPass() + : MachineFunctionPass(ID) { + initializeMachineCycleInfoWrapperPassPass(*PassRegistry::getPassRegistry()); +} + +INITIALIZE_PASS_BEGIN(MachineCycleInfoWrapperPass, "machine-cycles", + "Machine Cycle Info Analysis", true, true) +INITIALIZE_PASS_END(MachineCycleInfoWrapperPass, "machine-cycles", + "Machine Cycle Info Analysis", true, true) + +void MachineCycleInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + MachineFunctionPass::getAnalysisUsage(AU); +} + +bool MachineCycleInfoWrapperPass::runOnMachineFunction(MachineFunction &Func) { + CI.clear(); + + F = &Func; + CI.compute(Func); + return false; +} + +void MachineCycleInfoWrapperPass::print(raw_ostream &OS, const Module *) const { + OS << "MachineCycleInfo for function: " << F->getName() << "\n"; + CI.print(OS); +} + +void MachineCycleInfoWrapperPass::releaseMemory() { + CI.clear(); + F = nullptr; +} + +char MachineCycleInfoPrinterPass::ID = 0; + +MachineCycleInfoPrinterPass::MachineCycleInfoPrinterPass() + : MachineFunctionPass(ID) { + initializeMachineCycleInfoPrinterPassPass(*PassRegistry::getPassRegistry()); +} + +INITIALIZE_PASS_BEGIN(MachineCycleInfoPrinterPass, "print-machine-cycles", + "Print Machine Cycle Info Analysis", true, true) +INITIALIZE_PASS_DEPENDENCY(MachineCycleInfoWrapperPass) +INITIALIZE_PASS_END(MachineCycleInfoPrinterPass, "print-machine-cycles", + "Print Machine Cycle Info Analysis", true, true) + +void MachineCycleInfoPrinterPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); +} + +bool MachineCycleInfoPrinterPass::runOnMachineFunction(MachineFunction &F) { + auto &CI = getAnalysis(); + CI.print(errs()); + return false; +} diff --git a/llvm/lib/CodeGen/MachineSsaContext.cpp b/llvm/lib/CodeGen/MachineSsaContext.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/CodeGen/MachineSsaContext.cpp @@ -0,0 +1,50 @@ +//===- 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 +/// 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::incorporateFunction(const MachineFunction &F) { + RegInfo = &F.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) { + Out << ": "; + + MachineInstr *Instr = MRI->getUniqueVRegDef(Value); + Instr->print(Out); + } + }); +} diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt --- a/llvm/lib/IR/CMakeLists.txt +++ b/llvm/lib/IR/CMakeLists.txt @@ -27,6 +27,7 @@ Globals.cpp IRBuilder.cpp IRPrintingPasses.cpp + SsaContext.cpp InlineAsm.cpp Instruction.cpp Instructions.cpp diff --git a/llvm/lib/IR/SsaContext.cpp b/llvm/lib/IR/SsaContext.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/IR/SsaContext.cpp @@ -0,0 +1,45 @@ +//===- 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 defines a specialization of the GenericSsaContext +/// 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(); +} + +Printable SsaContext::print(Value *V) const { + return Printable([V](raw_ostream &Out) { V->print(Out); }); +} + +Printable SsaContext::print(Instruction *Inst) const { + return print(cast(Inst)); +} + +Printable SsaContext::print(BasicBlock *BB) const { + if (BB->hasName()) + return Printable([BB](raw_ostream &Out) { Out << BB->getName(); }); + + return Printable([BB](raw_ostream &Out) { + ModuleSlotTracker MST{BB->getParent()->getParent(), false}; + MST.incorporateFunction(*BB->getParent()); + Out << MST.getLocalSlot(BB); + }); +} diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -28,6 +28,7 @@ #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/CostModel.h" +#include "llvm/Analysis/CycleAnalysis.h" #include "llvm/Analysis/DDG.h" #include "llvm/Analysis/DDGPrinter.h" #include "llvm/Analysis/Delinearization.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -186,6 +186,7 @@ FUNCTION_ANALYSIS("assumptions", AssumptionAnalysis()) FUNCTION_ANALYSIS("block-freq", BlockFrequencyAnalysis()) FUNCTION_ANALYSIS("branch-prob", BranchProbabilityAnalysis()) +FUNCTION_ANALYSIS("cycles", CycleAnalysis()) FUNCTION_ANALYSIS("domtree", DominatorTreeAnalysis()) FUNCTION_ANALYSIS("postdomtree", PostDominatorTreeAnalysis()) FUNCTION_ANALYSIS("demanded-bits", DemandedBitsAnalysis()) @@ -303,6 +304,7 @@ FUNCTION_PASS("print", BlockFrequencyPrinterPass(dbgs())) FUNCTION_PASS("print", BranchProbabilityPrinterPass(dbgs())) FUNCTION_PASS("print", CostModelPrinterPass(dbgs())) +FUNCTION_PASS("print", CycleInfoPrinterPass(dbgs())) FUNCTION_PASS("print", DependenceAnalysisPrinterPass(dbgs())) FUNCTION_PASS("print", DivergenceAnalysisPrinterPass(dbgs())) FUNCTION_PASS("print", DominatorTreePrinterPass(dbgs())) diff --git a/llvm/test/Analysis/CycleInfo/basic.ll b/llvm/test/Analysis/CycleInfo/basic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/CycleInfo/basic.ll @@ -0,0 +1,302 @@ +; RUN: opt < %s -cycles -analyze -enable-new-pm=0 | FileCheck %s -check-prefix=CHECK +; RUN: opt < %s -disable-output -passes='print' 2>&1 | FileCheck %s -check-prefix=CHECK + +define void @empty() { +; CHECK-LABEL: CycleInfo for function: empty +; CHECK-NOT: depth + + ret void +} + +define void @simple() { +; CHECK-LABEL: CycleInfo for function: simple +; CHECK: depth=1: entries(loop) +entry: + br label %loop + +loop: + br i1 undef, label %loop, label %exit + +exit: + ret void +} + +define void @two_latches() { +; CHECK-LABEL: CycleInfo for function: two_latches +; CHECK: depth=1: entries(loop) loop_next +entry: + br label %loop + +loop: + br i1 undef, label %loop, label %loop_next + +loop_next: + br i1 undef, label %exit, label %loop + +exit: + ret void +} + +define void @nested_simple() { +; CHECK-LABEL: CycleInfo for function: nested_simple +; CHECK: depth=1: entries(outer_header) outer_latch inner +; CHECK: depth=2: entries(inner) +entry: + br label %outer_header + +outer_header: + br label %inner + +inner: + br i1 undef, label %inner, label %outer_latch + +outer_latch: + br i1 undef, label %outer_header, label %exit + +exit: + ret void +} + +define void @nested_outer_latch_in_inner_loop() { +; CHECK-LABEL: CycleInfo for function: nested_outer_latch_in_inner_loop +; CHECK: depth=1: entries(outer_header) inner_header inner_latch +; CHECK: depth=2: entries(inner_header) inner_latch +entry: + br label %outer_header + +outer_header: + br label %inner_header + +inner_header: + br i1 undef, label %inner_latch, label %outer_header + +inner_latch: + br i1 undef, label %exit, label %inner_header + +exit: + ret void +} + +define void @sibling_loops() { +; CHECK-LABEL: CycleInfo for function: sibling_loops +; CHECK-DAG: depth=1: entries(left) +; CHECK-DAG: depth=1: entries(right) +entry: + br i1 undef, label %left, label %right + +left: + br i1 undef, label %left, label %exit + +right: + br i1 undef, label %right, label %exit + +exit: + ret void +} + +define void @serial_loops() { +; CHECK-LABEL: CycleInfo for function: serial_loops +; CHECK-DAG: depth=1: entries(second) +; CHECK-DAG: depth=1: entries(first) +entry: + br label %first + +first: + br i1 undef, label %first, label %second + +second: + br i1 undef, label %second, label %exit + +exit: + ret void +} + +define void @nested_sibling_loops() { +; CHECK-LABEL: CycleInfo for function: nested_sibling_loops +; CHECK: depth=1: entries(outer_header) left right +; CHECK-DAG: depth=2: entries(right) +; CHECK-DAG: depth=2: entries(left) +entry: + br label %outer_header + +outer_header: + br i1 undef, label %left, label %right + +left: + switch i32 undef, label %exit [ i32 0, label %left + i32 1, label %outer_header ] + +right: + switch i32 undef, label %outer_header [ i32 0, label %exit + i32 1, label %right ] + +exit: + ret void +} + +define void @deeper_nest() { +; CHECK-LABEL: CycleInfo for function: deeper_nest +; CHECK: depth=1: entries(outer_header) outer_latch middle_header inner_header inner_latch +; CHECK: depth=2: entries(middle_header) inner_header inner_latch +; CHECK: depth=3: entries(inner_header) inner_latch +entry: + br label %outer_header + +outer_header: + br label %middle_header + +middle_header: + br label %inner_header + +inner_header: + br i1 undef, label %middle_header, label %inner_latch + +inner_latch: + br i1 undef, label %inner_header, label %outer_latch + +outer_latch: + br i1 undef, label %outer_header, label %exit + +exit: + ret void +} + +define void @irreducible_basic() { +; CHECK-LABEL: CycleInfo for function: irreducible_basic +; CHECK: depth=1: entries(right left) +entry: + br i1 undef, label %left, label %right + +left: + br i1 undef, label %right, label %exit + +right: + br i1 undef, label %left, label %exit + +exit: + ret void +} + +define void @irreducible_mess() { +; CHECK-LABEL: CycleInfo for function: irreducible_mess +; CHECK: depth=1: entries(B A) D C +; CHECK: depth=2: entries(D C A) +; CHECK: depth=3: entries(C A) +entry: + br i1 undef, label %A, label %B + +A: + br i1 undef, label %C, label %D + +B: + br i1 undef, label %C, label %D + +C: + switch i32 undef, label %A [ i32 0, label %D + i32 1, label %exit ] + +D: + switch i32 undef, label %B [ i32 0, label %C + i32 1, label %exit ] + +exit: + ret void +} + +define void @irreducible_into_simple_cycle() { +; CHECK-LABEL: CycleInfo for function: irreducible_into_simple_cycle +; CHECK: depth=1: entries(F C A) E D B +entry: + switch i32 undef, label %A [ i32 0, label %C + i32 1, label %F ] + +A: + br label %B + +B: + br label %C + +C: + br label %D + +D: + br i1 undef, label %E, label %exit + +E: + br label %F + +F: + br i1 undef, label %A, label %exit + +exit: + ret void +} + +define void @irreducible_mountain_bug() { +; CHECK-LABEL: CycleInfo for function: irreducible_mountain_bug +; CHECK: depth=1: entries(while.cond) +; CHECK: depth=2: entries(cond.end61 cond.true49) while.body63 while.cond47 +; CHECK: depth=3: entries(while.body63 cond.true49) while.cond47 +entry: + br i1 undef, label %if.end, label %if.then + +if.end: + br i1 undef, label %if.then7, label %if.else + +if.then7: + br label %if.end16 + +if.else: + br label %if.end16 + +if.end16: + br i1 undef, label %while.cond.preheader, label %if.then39 + +while.cond.preheader: + br label %while.cond + +while.cond: + br i1 undef, label %cond.true49, label %lor.rhs + +cond.true49: + br i1 undef, label %if.then69, label %while.body63 + +while.body63: + br i1 undef, label %exit, label %while.cond47 + +while.cond47: + br i1 undef, label %cond.true49, label %cond.end61 + +cond.end61: + br i1 undef, label %while.body63, label %while.cond + +if.then69: + br i1 undef, label %exit, label %while.cond + +lor.rhs: + br i1 undef, label %cond.end61, label %while.end76 + +while.end76: + br label %exit + +if.then39: + br i1 undef, label %exit, label %if.end.i145 + +if.end.i145: + br i1 undef, label %exit, label %if.end8.i149 + +if.end8.i149: + br label %exit + +if.then: + br i1 undef, label %exit, label %if.end.i + +if.end.i: + br i1 undef, label %exit, label %if.end8.i + +if.end8.i: + br label %exit + +exit: + ret void +} diff --git a/llvm/test/CodeGen/X86/cycle-info.mir b/llvm/test/CodeGen/X86/cycle-info.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/cycle-info.mir @@ -0,0 +1,628 @@ +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +... +--- +# 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: + RETQ + +...