| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,372 @@ | ||
| //===- llvm/Analysis/DDG.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 file defines the Data-Dependence Graph (DDG). | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_ANALYSIS_DDG_H | ||
| #define LLVM_ANALYSIS_DDG_H | ||
|
|
||
| #include "llvm/ADT/DirectedGraph.h" | ||
| #include "llvm/Analysis/DependenceAnalysis.h" | ||
| #include "llvm/Analysis/DependenceGraphBuilder.h" | ||
| #include "llvm/Analysis/LoopPass.h" | ||
| #include "llvm/IR/Instructions.h" | ||
| #include "llvm/Transforms/Scalar/LoopPassManager.h" | ||
| #include <unordered_map> | ||
|
|
||
| namespace llvm { | ||
| class DDGNode; | ||
| class DDGEdge; | ||
| using DDGNodeBase = DGNode<DDGNode, DDGEdge>; | ||
| using DDGEdgeBase = DGEdge<DDGNode, DDGEdge>; | ||
| using DDGBase = DirectedGraph<DDGNode, DDGEdge>; | ||
|
|
||
| /// Data Dependence Graph Node | ||
| /// The graph can represent the following types of nodes: | ||
| /// 1. Single instruction node containing just one instruction. | ||
| /// 2. Multiple instruction node where two or more instructions from | ||
| /// the same basic block are merged into one node. | ||
| class DDGNode : public DDGNodeBase { | ||
| public: | ||
| using InstructionListType = SmallVectorImpl<Instruction *>; | ||
|
|
||
| enum class NodeKind { | ||
| Unknown, | ||
| SingleInstruction, | ||
| MultiInstruction, | ||
| }; | ||
|
|
||
| DDGNode() = delete; | ||
| DDGNode(const NodeKind K) : DDGNodeBase(), Kind(K) {} | ||
| DDGNode(const DDGNode &N) : DDGNodeBase(N), Kind(N.Kind) {} | ||
| DDGNode(DDGNode &&N) : DDGNodeBase(std::move(N)), Kind(N.Kind) {} | ||
| virtual ~DDGNode() = 0; | ||
|
|
||
| DDGNode &operator=(const DDGNode &N) { | ||
| DGNode::operator=(N); | ||
| Kind = N.Kind; | ||
| return *this; | ||
| } | ||
|
|
||
| DDGNode &operator=(DDGNode &&N) { | ||
| DGNode::operator=(std::move(N)); | ||
| Kind = N.Kind; | ||
| return *this; | ||
| } | ||
|
|
||
| /// Getter for the kind of this node. | ||
| NodeKind getKind() const { return Kind; } | ||
|
|
||
| /// Collect a list of instructions, in \p IList, for which predicate \p Pred | ||
| /// evaluates to true when iterating over instructions of this node. Return | ||
| /// true if at least one instruction was collected, and false otherwise. | ||
| bool collectInstructions(llvm::function_ref<bool(Instruction *)> const &Pred, | ||
| InstructionListType &IList) const; | ||
|
|
||
| protected: | ||
| /// Setter for the kind of this node. | ||
| void setKind(NodeKind K) { Kind = K; } | ||
|
|
||
| private: | ||
| NodeKind Kind; | ||
| }; | ||
|
|
||
| /// Subclass of DDGNode representing single or multi-instruction nodes. | ||
| class SimpleDDGNode : public DDGNode { | ||
| public: | ||
| SimpleDDGNode() = delete; | ||
| SimpleDDGNode(Instruction &I); | ||
| SimpleDDGNode(const SimpleDDGNode &N); | ||
| SimpleDDGNode(SimpleDDGNode &&N); | ||
| ~SimpleDDGNode(); | ||
|
|
||
| SimpleDDGNode &operator=(const SimpleDDGNode &N) { | ||
| DDGNode::operator=(N); | ||
| InstList = N.InstList; | ||
| return *this; | ||
| } | ||
|
|
||
| SimpleDDGNode &operator=(SimpleDDGNode &&N) { | ||
| DDGNode::operator=(std::move(N)); | ||
| InstList = std::move(N.InstList); | ||
| return *this; | ||
| } | ||
|
|
||
| /// Get the list of instructions in this node. | ||
| const InstructionListType &getInstructions() const { | ||
| assert(!InstList.empty() && "Instruction List is empty."); | ||
| return InstList; | ||
| } | ||
| InstructionListType &getInstructions() { | ||
| return const_cast<InstructionListType &>( | ||
| static_cast<const SimpleDDGNode *>(this)->getInstructions()); | ||
| } | ||
|
|
||
| /// Get the first/last instruction in the node. | ||
| Instruction *getFirstInstruction() const { return getInstructions().front(); } | ||
| Instruction *getLastInstruction() const { return getInstructions().back(); } | ||
|
|
||
| /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc. | ||
| static bool classof(const DDGNode *N) { | ||
| return N->getKind() == NodeKind::SingleInstruction || | ||
| N->getKind() == NodeKind::MultiInstruction; | ||
| } | ||
| static bool classof(const SimpleDDGNode *N) { return true; } | ||
|
|
||
| private: | ||
| /// Append the list of instructions in \p Input to this node. | ||
| void appendInstructions(const InstructionListType &Input) { | ||
| setKind((InstList.size() == 0 && Input.size() == 1) | ||
| ? NodeKind::SingleInstruction | ||
| : NodeKind::MultiInstruction); | ||
| InstList.insert(InstList.end(), Input.begin(), Input.end()); | ||
| } | ||
| void appendInstructions(const SimpleDDGNode &Input) { | ||
| appendInstructions(Input.getInstructions()); | ||
| } | ||
|
|
||
| /// List of instructions associated with a single or multi-instruction node. | ||
| SmallVector<Instruction *, 2> InstList; | ||
| }; | ||
|
|
||
| /// Data Dependency Graph Edge. | ||
| /// An edge in the DDG can represent a def-use relationship or | ||
| /// a memory dependence based on the result of DependenceAnalysis. | ||
| class DDGEdge : public DDGEdgeBase { | ||
| public: | ||
| /// The kind of edge in the DDG | ||
| enum class EdgeKind { Unknown, RegisterDefUse, MemoryDependence }; | ||
|
|
||
| explicit DDGEdge(DDGNode &N) = delete; | ||
| DDGEdge(DDGNode &N, EdgeKind K) : DDGEdgeBase(N), Kind(K) {} | ||
| DDGEdge(const DDGEdge &E) : DDGEdgeBase(E), Kind(E.getKind()) {} | ||
| DDGEdge(DDGEdge &&E) : DDGEdgeBase(std::move(E)), Kind(E.Kind) {} | ||
| DDGEdge &operator=(const DDGEdge &E) { | ||
| DDGEdgeBase::operator=(E); | ||
| Kind = E.Kind; | ||
| return *this; | ||
| } | ||
|
|
||
| DDGEdge &operator=(DDGEdge &&E) { | ||
| DDGEdgeBase::operator=(std::move(E)); | ||
| Kind = E.Kind; | ||
| return *this; | ||
| } | ||
|
|
||
| /// Get the edge kind | ||
| EdgeKind getKind() const { return Kind; }; | ||
|
|
||
| /// Return true if this is a def-use edge, and false otherwise. | ||
| bool isDefUse() const { return Kind == EdgeKind::RegisterDefUse; } | ||
|
|
||
| /// Return true if this is a memory dependence edge, and false otherwise. | ||
| bool isMemoryDependence() const { return Kind == EdgeKind::MemoryDependence; } | ||
|
|
||
| private: | ||
| EdgeKind Kind; | ||
| }; | ||
|
|
||
| /// Encapsulate some common data and functionality needed for different | ||
| /// variations of data dependence graphs. | ||
| template <typename NodeType> class DependenceGraphInfo { | ||
| public: | ||
| using DependenceList = SmallVector<std::unique_ptr<Dependence>, 1>; | ||
|
|
||
| DependenceGraphInfo() = delete; | ||
| DependenceGraphInfo(const DependenceGraphInfo &G) = delete; | ||
| DependenceGraphInfo(const std::string &N, const DependenceInfo &DepInfo) | ||
| : Name(N), DI(DepInfo) {} | ||
| DependenceGraphInfo(DependenceGraphInfo &&G) | ||
| : Name(std::move(G.Name)), DI(std::move(G.DI)) {} | ||
| virtual ~DependenceGraphInfo() {} | ||
|
|
||
| /// Return the label that is used to name this graph. | ||
| const StringRef getName() const { return Name; } | ||
|
|
||
| protected: | ||
| // Name of the graph. | ||
| std::string Name; | ||
|
|
||
| // Store a copy of DependenceInfo in the graph, so that individual memory | ||
| // dependencies don't need to be stored. Instead when the dependence is | ||
| // queried it is recomputed using @DI. | ||
| const DependenceInfo DI; | ||
| }; | ||
|
|
||
| using DDGInfo = DependenceGraphInfo<DDGNode>; | ||
|
|
||
| /// Data Dependency Graph | ||
| class DataDependenceGraph : public DDGBase, public DDGInfo { | ||
| friend class DDGBuilder; | ||
|
|
||
| public: | ||
| using NodeType = DDGNode; | ||
| using EdgeType = DDGEdge; | ||
|
|
||
| DataDependenceGraph() = delete; | ||
| DataDependenceGraph(const DataDependenceGraph &G) = delete; | ||
| DataDependenceGraph(DataDependenceGraph &&G) | ||
| : DDGBase(std::move(G)), DDGInfo(std::move(G)) {} | ||
| DataDependenceGraph(Function &F, DependenceInfo &DI); | ||
| DataDependenceGraph(const Loop &L, DependenceInfo &DI); | ||
| ~DataDependenceGraph(); | ||
| }; | ||
|
|
||
| /// Concrete implementation of a pure data dependence graph builder. This class | ||
| /// provides custom implementation for the pure-virtual functions used in the | ||
| /// generic dependence graph build algorithm. | ||
| /// | ||
| /// For information about time complexity of the build algorithm see the | ||
| /// comments near the declaration of AbstractDependenceGraphBuilder. | ||
| class DDGBuilder : public AbstractDependenceGraphBuilder<DataDependenceGraph> { | ||
| public: | ||
| DDGBuilder(DataDependenceGraph &G, DependenceInfo &D, | ||
| const BasicBlockListType &BBs) | ||
| : AbstractDependenceGraphBuilder(G, D, BBs) {} | ||
| DDGNode &createFineGrainedNode(Instruction &I) final override { | ||
| auto *SN = new SimpleDDGNode(I); | ||
| assert(SN && "Failed to allocate memory for simple DDG node."); | ||
| Graph.addNode(*SN); | ||
| return *SN; | ||
| } | ||
| DDGEdge &createDefUseEdge(DDGNode &Src, DDGNode &Tgt) final override { | ||
| auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::RegisterDefUse); | ||
| assert(E && "Failed to allocate memory for edge"); | ||
| Graph.connect(Src, Tgt, *E); | ||
| return *E; | ||
| } | ||
| DDGEdge &createMemoryEdge(DDGNode &Src, DDGNode &Tgt) final override { | ||
| auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::MemoryDependence); | ||
| assert(E && "Failed to allocate memory for edge"); | ||
| Graph.connect(Src, Tgt, *E); | ||
| return *E; | ||
| } | ||
| }; | ||
|
|
||
| raw_ostream &operator<<(raw_ostream &OS, const DDGNode &N); | ||
| raw_ostream &operator<<(raw_ostream &OS, const DDGNode::NodeKind K); | ||
| raw_ostream &operator<<(raw_ostream &OS, const DDGEdge &E); | ||
| raw_ostream &operator<<(raw_ostream &OS, const DDGEdge::EdgeKind K); | ||
| raw_ostream &operator<<(raw_ostream &OS, const DataDependenceGraph &G); | ||
|
|
||
| //===--------------------------------------------------------------------===// | ||
| // DDG Analysis Passes | ||
| //===--------------------------------------------------------------------===// | ||
|
|
||
| /// Analysis pass that builds the DDG for a loop. | ||
| class DDGAnalysis : public AnalysisInfoMixin<DDGAnalysis> { | ||
| public: | ||
| using Result = std::unique_ptr<DataDependenceGraph>; | ||
| Result run(Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR); | ||
|
|
||
| private: | ||
| friend AnalysisInfoMixin<DDGAnalysis>; | ||
| static AnalysisKey Key; | ||
| }; | ||
|
|
||
| /// Textual printer pass for the DDG of a loop. | ||
| class DDGAnalysisPrinterPass : public PassInfoMixin<DDGAnalysisPrinterPass> { | ||
| public: | ||
| explicit DDGAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {} | ||
| PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM, | ||
| LoopStandardAnalysisResults &AR, LPMUpdater &U); | ||
|
|
||
| private: | ||
| raw_ostream &OS; | ||
| }; | ||
|
|
||
| //===--------------------------------------------------------------------===// | ||
| // GraphTraits specializations for the DDG | ||
| //===--------------------------------------------------------------------===// | ||
|
|
||
| /// non-const versions of the grapth trait specializations for DDG | ||
| template <> struct GraphTraits<DDGNode *> { | ||
| using NodeRef = DDGNode *; | ||
|
|
||
| static DDGNode *DDGGetTargetNode(DGEdge<DDGNode, DDGEdge> *P) { | ||
| return &P->getTargetNode(); | ||
| } | ||
|
|
||
| // Provide a mapped iterator so that the GraphTrait-based implementations can | ||
| // find the target nodes without having to explicitly go through the edges. | ||
| using ChildIteratorType = | ||
| mapped_iterator<DDGNode::iterator, decltype(&DDGGetTargetNode)>; | ||
| using ChildEdgeIteratorType = DDGNode::iterator; | ||
|
|
||
| static NodeRef getEntryNode(NodeRef N) { return N; } | ||
| static ChildIteratorType child_begin(NodeRef N) { | ||
| return ChildIteratorType(N->begin(), &DDGGetTargetNode); | ||
| } | ||
| static ChildIteratorType child_end(NodeRef N) { | ||
| return ChildIteratorType(N->end(), &DDGGetTargetNode); | ||
| } | ||
|
|
||
| static ChildEdgeIteratorType child_edge_begin(NodeRef N) { | ||
| return N->begin(); | ||
| } | ||
| static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); } | ||
| }; | ||
|
|
||
| template <> | ||
| struct GraphTraits<DataDependenceGraph *> : public GraphTraits<DDGNode *> { | ||
| using nodes_iterator = DataDependenceGraph::iterator; | ||
| static NodeRef getEntryNode(DataDependenceGraph *DG) { return *DG->begin(); } | ||
| static nodes_iterator nodes_begin(DataDependenceGraph *DG) { | ||
| return DG->begin(); | ||
| } | ||
| static nodes_iterator nodes_end(DataDependenceGraph *DG) { return DG->end(); } | ||
| }; | ||
|
|
||
| /// const versions of the grapth trait specializations for DDG | ||
| template <> struct GraphTraits<const DDGNode *> { | ||
| using NodeRef = const DDGNode *; | ||
|
|
||
| static const DDGNode *DDGGetTargetNode(const DGEdge<DDGNode, DDGEdge> *P) { | ||
| return &P->getTargetNode(); | ||
| } | ||
|
|
||
| // Provide a mapped iterator so that the GraphTrait-based implementations can | ||
| // find the target nodes without having to explicitly go through the edges. | ||
| using ChildIteratorType = | ||
| mapped_iterator<DDGNode::const_iterator, decltype(&DDGGetTargetNode)>; | ||
| using ChildEdgeIteratorType = DDGNode::const_iterator; | ||
|
|
||
| static NodeRef getEntryNode(NodeRef N) { return N; } | ||
| static ChildIteratorType child_begin(NodeRef N) { | ||
| return ChildIteratorType(N->begin(), &DDGGetTargetNode); | ||
| } | ||
| static ChildIteratorType child_end(NodeRef N) { | ||
| return ChildIteratorType(N->end(), &DDGGetTargetNode); | ||
| } | ||
|
|
||
| static ChildEdgeIteratorType child_edge_begin(NodeRef N) { | ||
| return N->begin(); | ||
| } | ||
| static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); } | ||
| }; | ||
|
|
||
| template <> | ||
| struct GraphTraits<const DataDependenceGraph *> | ||
| : public GraphTraits<const DDGNode *> { | ||
| using nodes_iterator = DataDependenceGraph::const_iterator; | ||
| static NodeRef getEntryNode(const DataDependenceGraph *DG) { | ||
| return *DG->begin(); | ||
| } | ||
| static nodes_iterator nodes_begin(const DataDependenceGraph *DG) { | ||
| return DG->begin(); | ||
| } | ||
| static nodes_iterator nodes_end(const DataDependenceGraph *DG) { | ||
| return DG->end(); | ||
| } | ||
| }; | ||
|
|
||
| } // namespace llvm | ||
|
|
||
| #endif // LLVM_ANALYSIS_DDG_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| //===- llvm/Analysis/DependenceGraphBuilder.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 file defines a builder interface that can be used to populate dependence | ||
| // graphs such as DDG and PDG. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_ANALYSIS_DEPENDENCE_GRAPH_BUILDER_H | ||
| #define LLVM_ANALYSIS_DEPENDENCE_GRAPH_BUILDER_H | ||
|
|
||
| #include "llvm/ADT/EquivalenceClasses.h" | ||
| #include "llvm/Analysis/DependenceAnalysis.h" | ||
| #include "llvm/IR/BasicBlock.h" | ||
| #include "llvm/IR/Instructions.h" | ||
|
|
||
| namespace llvm { | ||
|
|
||
| /// This abstract builder class defines a set of high-level steps for creating | ||
| /// DDG-like graphs. The client code is expected to inherit from this class and | ||
| /// define concrete implementation for each of the pure virtual functions used | ||
| /// in the high-level algorithm. | ||
| template <class GraphType> class AbstractDependenceGraphBuilder { | ||
| protected: | ||
| using BasicBlockListType = SmallVectorImpl<BasicBlock *>; | ||
|
|
||
| private: | ||
| using NodeType = typename GraphType::NodeType; | ||
| using EdgeType = typename GraphType::EdgeType; | ||
|
|
||
| public: | ||
| using ClassesType = EquivalenceClasses<BasicBlock *>; | ||
| using NodeListType = SmallVector<NodeType *, 4>; | ||
|
|
||
| AbstractDependenceGraphBuilder(GraphType &G, DependenceInfo &D, | ||
| const BasicBlockListType &BBs) | ||
| : Graph(G), DI(D), BBList(BBs) {} | ||
| virtual ~AbstractDependenceGraphBuilder() {} | ||
|
|
||
| /// The main entry to the graph construction algorithm. It starts by | ||
| /// creating nodes in increasing order of granularity and then | ||
| /// adds def-use and memory edges. | ||
| /// | ||
| /// The algorithmic complexity of this implementation is O(V^2 * I^2), where V | ||
| /// is the number of vertecies (nodes) and I is the number of instructions in | ||
| /// each node. The total number of instructions, N, is equal to V * I, | ||
| /// therefore the worst-case time complexity is O(N^2). The average time | ||
| /// complexity is O((N^2)/2). | ||
| void populate() { | ||
| createFineGrainedNodes(); | ||
| createDefUseEdges(); | ||
| createMemoryDependencyEdges(); | ||
| } | ||
|
|
||
| /// Create fine grained nodes. These are typically atomic nodes that | ||
| /// consist of a single instruction. | ||
| void createFineGrainedNodes(); | ||
|
|
||
| /// Analyze the def-use chains and create edges from the nodes containing | ||
| /// definitions to the nodes containing the uses. | ||
| void createDefUseEdges(); | ||
|
|
||
| /// Analyze data dependencies that exist between memory loads or stores, | ||
| /// in the graph nodes and create edges between them. | ||
| void createMemoryDependencyEdges(); | ||
|
|
||
| protected: | ||
| /// Create an atomic node in the graph given a single instruction. | ||
| virtual NodeType &createFineGrainedNode(Instruction &I) = 0; | ||
|
|
||
| /// Create a def-use edge going from \p Src to \p Tgt. | ||
| virtual EdgeType &createDefUseEdge(NodeType &Src, NodeType &Tgt) = 0; | ||
|
|
||
| /// Create a memory dependence edge going from \p Src to \p Tgt. | ||
| virtual EdgeType &createMemoryEdge(NodeType &Src, NodeType &Tgt) = 0; | ||
|
|
||
| /// Deallocate memory of edge \p E. | ||
| virtual void destroyEdge(EdgeType &E) { delete &E; } | ||
|
|
||
| /// Deallocate memory of node \p N. | ||
| virtual void destroyNode(NodeType &N) { delete &N; } | ||
|
|
||
| /// Map types to map instructions to nodes used when populating the graph. | ||
| using InstToNodeMap = DenseMap<Instruction *, NodeType *>; | ||
|
|
||
| /// Reference to the graph that gets built by a concrete implementation of | ||
| /// this builder. | ||
| GraphType &Graph; | ||
|
|
||
| /// Dependence information used to create memory dependence edges in the | ||
| /// graph. | ||
| DependenceInfo &DI; | ||
|
|
||
| /// The list of basic blocks to consider when building the graph. | ||
| const BasicBlockListType &BBList; | ||
|
|
||
| /// A mapping from instructions to the corresponding nodes in the graph. | ||
| InstToNodeMap IMap; | ||
| }; | ||
|
|
||
| } // namespace llvm | ||
|
|
||
| #endif // LLVM_ANALYSIS_DEPENDENCE_GRAPH_BUILDER_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| //===- DDG.cpp - Data Dependence Graph -------------------------------------==// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // The implementation for the data dependence graph. | ||
| //===----------------------------------------------------------------------===// | ||
| #include "llvm/Analysis/DDG.h" | ||
| #include "llvm/Analysis/LoopInfo.h" | ||
|
|
||
| using namespace llvm; | ||
|
|
||
| #define DEBUG_TYPE "ddg" | ||
|
|
||
| template class llvm::DGEdge<DDGNode, DDGEdge>; | ||
| template class llvm::DGNode<DDGNode, DDGEdge>; | ||
| template class llvm::DirectedGraph<DDGNode, DDGEdge>; | ||
|
|
||
| //===--------------------------------------------------------------------===// | ||
| // DDGNode implementation | ||
| //===--------------------------------------------------------------------===// | ||
| DDGNode::~DDGNode() {} | ||
|
|
||
| bool DDGNode::collectInstructions( | ||
| llvm::function_ref<bool(Instruction *)> const &Pred, | ||
| InstructionListType &IList) const { | ||
| assert(IList.empty() && "Expected the IList to be empty on entry."); | ||
| if (isa<SimpleDDGNode>(this)) { | ||
| for (auto *I : cast<const SimpleDDGNode>(this)->getInstructions()) | ||
| if (Pred(I)) | ||
| IList.push_back(I); | ||
| } else | ||
| llvm_unreachable("unimplemented type of node"); | ||
| return !IList.empty(); | ||
| } | ||
|
|
||
| raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGNode::NodeKind K) { | ||
| const char *Out; | ||
| switch (K) { | ||
| case DDGNode::NodeKind::SingleInstruction: | ||
| Out = "single-instruction"; | ||
| break; | ||
| case DDGNode::NodeKind::MultiInstruction: | ||
| Out = "multi-instruction"; | ||
| break; | ||
| case DDGNode::NodeKind::Unknown: | ||
| Out = "??"; | ||
| break; | ||
| } | ||
| OS << Out; | ||
| return OS; | ||
| } | ||
|
|
||
| raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGNode &N) { | ||
| OS << "Node Address:" << &N << ":" << N.getKind() << "\n"; | ||
| if (isa<SimpleDDGNode>(N)) { | ||
| OS << " Instructions:\n"; | ||
| for (auto *I : cast<const SimpleDDGNode>(N).getInstructions()) | ||
| OS.indent(2) << *I << "\n"; | ||
| } else | ||
| llvm_unreachable("unimplemented type of node"); | ||
|
|
||
| OS << (N.getEdges().empty() ? " Edges:none!\n" : " Edges:\n"); | ||
| for (auto &E : N.getEdges()) | ||
| OS.indent(2) << *E; | ||
| return OS; | ||
| } | ||
|
|
||
| //===--------------------------------------------------------------------===// | ||
| // SimpleDDGNode implementation | ||
| //===--------------------------------------------------------------------===// | ||
|
|
||
| SimpleDDGNode::SimpleDDGNode(Instruction &I) | ||
| : DDGNode(NodeKind::SingleInstruction), InstList() { | ||
| assert(InstList.empty() && "Expected empty list."); | ||
| InstList.push_back(&I); | ||
| } | ||
|
|
||
| SimpleDDGNode::SimpleDDGNode(const SimpleDDGNode &N) | ||
| : DDGNode(N), InstList(N.InstList) { | ||
| assert(((getKind() == NodeKind::SingleInstruction && InstList.size() == 1) || | ||
| (getKind() == NodeKind::MultiInstruction && InstList.size() > 1)) && | ||
| "constructing from invalid simple node."); | ||
| } | ||
|
|
||
| SimpleDDGNode::SimpleDDGNode(SimpleDDGNode &&N) | ||
| : DDGNode(std::move(N)), InstList(std::move(N.InstList)) { | ||
| assert(((getKind() == NodeKind::SingleInstruction && InstList.size() == 1) || | ||
| (getKind() == NodeKind::MultiInstruction && InstList.size() > 1)) && | ||
| "constructing from invalid simple node."); | ||
| } | ||
|
|
||
| SimpleDDGNode::~SimpleDDGNode() { InstList.clear(); } | ||
|
|
||
| //===--------------------------------------------------------------------===// | ||
| // DDGEdge implementation | ||
| //===--------------------------------------------------------------------===// | ||
|
|
||
| raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGEdge::EdgeKind K) { | ||
| const char *Out; | ||
| switch (K) { | ||
| case DDGEdge::EdgeKind::RegisterDefUse: | ||
| Out = "def-use"; | ||
| break; | ||
| case DDGEdge::EdgeKind::MemoryDependence: | ||
| Out = "memory"; | ||
| break; | ||
| case DDGEdge::EdgeKind::Unknown: | ||
| Out = "??"; | ||
| break; | ||
| } | ||
| OS << Out; | ||
| return OS; | ||
| } | ||
|
|
||
| raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGEdge &E) { | ||
| OS << "[" << E.getKind() << "] to " << &E.getTargetNode() << "\n"; | ||
| return OS; | ||
| } | ||
|
|
||
| //===--------------------------------------------------------------------===// | ||
| // DataDependenceGraph implementation | ||
| //===--------------------------------------------------------------------===// | ||
| using BasicBlockListType = SmallVector<BasicBlock *, 8>; | ||
|
|
||
| DataDependenceGraph::DataDependenceGraph(Function &F, DependenceInfo &D) | ||
| : DependenceGraphInfo(F.getName().str(), D) { | ||
| BasicBlockListType BBList; | ||
| for (auto &BB : F.getBasicBlockList()) | ||
| BBList.push_back(&BB); | ||
| DDGBuilder(*this, D, BBList).populate(); | ||
| } | ||
|
|
||
| DataDependenceGraph::DataDependenceGraph(const Loop &L, DependenceInfo &D) | ||
| : DependenceGraphInfo(Twine(L.getHeader()->getParent()->getName() + "." + | ||
| L.getHeader()->getName()) | ||
| .str(), | ||
| D) { | ||
| BasicBlockListType BBList; | ||
| for (BasicBlock *BB : L.blocks()) | ||
| BBList.push_back(BB); | ||
| DDGBuilder(*this, D, BBList).populate(); | ||
| } | ||
|
|
||
| DataDependenceGraph::~DataDependenceGraph() { | ||
| for (auto *N : Nodes) { | ||
| for (auto *E : *N) | ||
| delete E; | ||
| delete N; | ||
| } | ||
| } | ||
|
|
||
| raw_ostream &llvm::operator<<(raw_ostream &OS, const DataDependenceGraph &G) { | ||
| for (auto *Node : G) | ||
| OS << *Node << "\n"; | ||
| return OS; | ||
| } | ||
|
|
||
| //===--------------------------------------------------------------------===// | ||
| // DDG Analysis Passes | ||
| //===--------------------------------------------------------------------===// | ||
|
|
||
| /// DDG as a loop pass. | ||
| DDGAnalysis::Result DDGAnalysis::run(Loop &L, LoopAnalysisManager &AM, | ||
| LoopStandardAnalysisResults &AR) { | ||
| Function *F = L.getHeader()->getParent(); | ||
| DependenceInfo DI(F, &AR.AA, &AR.SE, &AR.LI); | ||
| return std::make_unique<DataDependenceGraph>(L, DI); | ||
| } | ||
| AnalysisKey DDGAnalysis::Key; | ||
|
|
||
| PreservedAnalyses DDGAnalysisPrinterPass::run(Loop &L, LoopAnalysisManager &AM, | ||
| LoopStandardAnalysisResults &AR, | ||
| LPMUpdater &U) { | ||
| OS << "'DDG' for loop '" << L.getHeader()->getName() << "':\n"; | ||
| OS << *AM.getResult<DDGAnalysis>(L, AR); | ||
| return PreservedAnalyses::all(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| //===- DependenceGraphBuilder.cpp ------------------------------------------==// | ||
| // | ||
| // 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 implements common steps of the build algorithm for construction | ||
| // of dependence graphs such as DDG and PDG. | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "llvm/Analysis/DependenceGraphBuilder.h" | ||
| #include "llvm/ADT/SCCIterator.h" | ||
| #include "llvm/ADT/Statistic.h" | ||
| #include "llvm/Analysis/DDG.h" | ||
|
|
||
| using namespace llvm; | ||
|
|
||
| #define DEBUG_TYPE "dgb" | ||
|
|
||
| STATISTIC(TotalGraphs, "Number of dependence graphs created."); | ||
| STATISTIC(TotalDefUseEdges, "Number of def-use edges created."); | ||
| STATISTIC(TotalMemoryEdges, "Number of memory dependence edges created."); | ||
| STATISTIC(TotalFineGrainedNodes, "Number of fine-grained nodes created."); | ||
| STATISTIC(TotalConfusedEdges, | ||
| "Number of confused memory dependencies between two nodes."); | ||
| STATISTIC(TotalEdgeReversals, | ||
| "Number of times the source and sink of dependence was reversed to " | ||
| "expose cycles in the graph."); | ||
|
|
||
| using InstructionListType = SmallVector<Instruction *, 2>; | ||
|
|
||
| //===--------------------------------------------------------------------===// | ||
| // AbstractDependenceGraphBuilder implementation | ||
| //===--------------------------------------------------------------------===// | ||
|
|
||
| template <class G> | ||
| void AbstractDependenceGraphBuilder<G>::createFineGrainedNodes() { | ||
| ++TotalGraphs; | ||
| assert(IMap.empty() && "Expected empty instruction map at start"); | ||
| for (BasicBlock *BB : BBList) | ||
| for (Instruction &I : *BB) { | ||
| auto &NewNode = createFineGrainedNode(I); | ||
| IMap.insert(std::make_pair(&I, &NewNode)); | ||
| ++TotalFineGrainedNodes; | ||
| } | ||
| } | ||
|
|
||
| template <class G> void AbstractDependenceGraphBuilder<G>::createDefUseEdges() { | ||
| for (NodeType *N : Graph) { | ||
| InstructionListType SrcIList; | ||
| N->collectInstructions([](const Instruction *I) { return true; }, SrcIList); | ||
|
|
||
| // Use a set to mark the targets that we link to N, so we don't add | ||
| // duplicate def-use edges when more than one instruction in a target node | ||
| // use results of instructions that are contained in N. | ||
| SmallPtrSet<NodeType *, 4> VisitedTargets; | ||
|
|
||
| for (Instruction *II : SrcIList) { | ||
| for (User *U : II->users()) { | ||
| Instruction *UI = dyn_cast<Instruction>(U); | ||
| if (!UI) | ||
| continue; | ||
| NodeType *DstNode = nullptr; | ||
| if (IMap.find(UI) != IMap.end()) | ||
| DstNode = IMap.find(UI)->second; | ||
|
|
||
| // In the case of loops, the scope of the subgraph is all the | ||
| // basic blocks (and instructions within them) belonging to the loop. We | ||
| // simply ignore all the edges coming from (or going into) instructions | ||
| // or basic blocks outside of this range. | ||
| if (!DstNode) { | ||
| LLVM_DEBUG( | ||
| dbgs() | ||
| << "skipped def-use edge since the sink" << *UI | ||
| << " is outside the range of instructions being considered.\n"); | ||
| continue; | ||
| } | ||
|
|
||
| // Self dependencies are ignored because they are redundant and | ||
| // uninteresting. | ||
| if (DstNode == N) { | ||
| LLVM_DEBUG(dbgs() | ||
| << "skipped def-use edge since the sink and the source (" | ||
| << N << ") are the same.\n"); | ||
| continue; | ||
| } | ||
|
|
||
| if (VisitedTargets.insert(DstNode).second) { | ||
| createDefUseEdge(*N, *DstNode); | ||
| ++TotalDefUseEdges; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| template <class G> | ||
| void AbstractDependenceGraphBuilder<G>::createMemoryDependencyEdges() { | ||
| using DGIterator = typename G::iterator; | ||
| auto isMemoryAccess = [](const Instruction *I) { | ||
| return I->mayReadOrWriteMemory(); | ||
| }; | ||
| for (DGIterator SrcIt = Graph.begin(), E = Graph.end(); SrcIt != E; ++SrcIt) { | ||
| InstructionListType SrcIList; | ||
| (*SrcIt)->collectInstructions(isMemoryAccess, SrcIList); | ||
| if (SrcIList.empty()) | ||
| continue; | ||
|
|
||
| for (DGIterator DstIt = SrcIt; DstIt != E; ++DstIt) { | ||
| if (**SrcIt == **DstIt) | ||
| continue; | ||
| InstructionListType DstIList; | ||
| (*DstIt)->collectInstructions(isMemoryAccess, DstIList); | ||
| if (DstIList.empty()) | ||
| continue; | ||
| bool ForwardEdgeCreated = false; | ||
| bool BackwardEdgeCreated = false; | ||
| for (Instruction *ISrc : SrcIList) { | ||
| for (Instruction *IDst : DstIList) { | ||
| auto D = DI.depends(ISrc, IDst, true); | ||
| if (!D) | ||
| continue; | ||
|
|
||
| // If we have a dependence with its left-most non-'=' direction | ||
| // being '>' we need to reverse the direction of the edge, because | ||
| // the source of the dependence cannot occur after the sink. For | ||
| // confused dependencies, we will create edges in both directions to | ||
| // represent the possibility of a cycle. | ||
|
|
||
| auto createConfusedEdges = [&](NodeType &Src, NodeType &Dst) { | ||
| if (!ForwardEdgeCreated) { | ||
| createMemoryEdge(Src, Dst); | ||
| ++TotalMemoryEdges; | ||
| } | ||
| if (!BackwardEdgeCreated) { | ||
| createMemoryEdge(Dst, Src); | ||
| ++TotalMemoryEdges; | ||
| } | ||
| ForwardEdgeCreated = BackwardEdgeCreated = true; | ||
| ++TotalConfusedEdges; | ||
| }; | ||
|
|
||
| auto createForwardEdge = [&](NodeType &Src, NodeType &Dst) { | ||
| if (!ForwardEdgeCreated) { | ||
| createMemoryEdge(Src, Dst); | ||
| ++TotalMemoryEdges; | ||
| } | ||
| ForwardEdgeCreated = true; | ||
| }; | ||
|
|
||
| auto createBackwardEdge = [&](NodeType &Src, NodeType &Dst) { | ||
| if (!BackwardEdgeCreated) { | ||
| createMemoryEdge(Dst, Src); | ||
| ++TotalMemoryEdges; | ||
| } | ||
| BackwardEdgeCreated = true; | ||
| }; | ||
|
|
||
| if (D->isConfused()) | ||
| createConfusedEdges(**SrcIt, **DstIt); | ||
| else if (D->isOrdered() && !D->isLoopIndependent()) { | ||
| bool ReversedEdge = false; | ||
| for (unsigned Level = 1; Level <= D->getLevels(); ++Level) { | ||
| if (D->getDirection(Level) == Dependence::DVEntry::EQ) | ||
| continue; | ||
| else if (D->getDirection(Level) == Dependence::DVEntry::GT) { | ||
| createBackwardEdge(**SrcIt, **DstIt); | ||
| ReversedEdge = true; | ||
| ++TotalEdgeReversals; | ||
| break; | ||
| } else if (D->getDirection(Level) == Dependence::DVEntry::LT) | ||
| break; | ||
| else { | ||
| createConfusedEdges(**SrcIt, **DstIt); | ||
| break; | ||
| } | ||
| } | ||
| if (!ReversedEdge) | ||
| createForwardEdge(**SrcIt, **DstIt); | ||
| } else | ||
| createForwardEdge(**SrcIt, **DstIt); | ||
|
|
||
| // Avoid creating duplicate edges. | ||
| if (ForwardEdgeCreated && BackwardEdgeCreated) | ||
| break; | ||
| } | ||
|
|
||
| // If we've created edges in both directions, there is no more | ||
| // unique edge that we can create between these two nodes, so we | ||
| // can exit early. | ||
| if (ForwardEdgeCreated && BackwardEdgeCreated) | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| template class llvm::AbstractDependenceGraphBuilder<DataDependenceGraph>; | ||
| template class llvm::DependenceGraphInfo<DDGNode>; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| ; RUN: opt < %s -disable-output "-passes=print<ddg>" 2>&1 | FileCheck %s | ||
|
|
||
| ; CHECK-LABEL: 'DDG' for loop 'test1.for.body': | ||
| ; CHECK: Node Address:[[N1:0x[0-9a-f]*]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %i.02 = phi i64 [ %inc, %test1.for.body ], [ 0, %test1.for.body.preheader ] | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N2:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N3:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N4:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N4]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx = getelementptr inbounds float, float* %b, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N5:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N5]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %0 = load float, float* %arrayidx, align 4 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N6:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N7:0x[0-9a-f]*]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %conv = uitofp i64 %n to float | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N6]] | ||
|
|
||
| ; CHECK: Node Address:[[N6]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %add = fadd float %0, %conv | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N8:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N3]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx1 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N8]] | ||
|
|
||
| ; CHECK: Node Address:[[N8]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: store float %add, float* %arrayidx1, align 4 | ||
| ; CHECK-NEXT: Edges:none! | ||
|
|
||
| ; CHECK: Node Address:[[N2]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %inc = add i64 %i.02, 1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N9:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N1]] | ||
|
|
||
| ; CHECK: Node Address:[[N9]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %exitcond = icmp ne i64 %inc, %n | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N10:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N10]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: br i1 %exitcond, label %test1.for.body, label %for.end.loopexit | ||
| ; CHECK-NEXT: Edges:none! | ||
|
|
||
| ;; No memory dependencies. | ||
| ;; void test1(unsigned long n, float * restrict a, float * restrict b) { | ||
| ;; for (unsigned long i = 0; i < n; i++) | ||
| ;; a[i] = b[i] + n; | ||
| ;; } | ||
|
|
||
| define void @test1(i64 %n, float* noalias %a, float* noalias %b) { | ||
| entry: | ||
| %exitcond1 = icmp ne i64 0, %n | ||
| br i1 %exitcond1, label %test1.for.body, label %for.end | ||
|
|
||
| test1.for.body: ; preds = %entry, %test1.for.body | ||
| %i.02 = phi i64 [ %inc, %test1.for.body ], [ 0, %entry ] | ||
| %arrayidx = getelementptr inbounds float, float* %b, i64 %i.02 | ||
| %0 = load float, float* %arrayidx, align 4 | ||
| %conv = uitofp i64 %n to float | ||
| %add = fadd float %0, %conv | ||
| %arrayidx1 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| store float %add, float* %arrayidx1, align 4 | ||
| %inc = add i64 %i.02, 1 | ||
| %exitcond = icmp ne i64 %inc, %n | ||
| br i1 %exitcond, label %test1.for.body, label %for.end | ||
|
|
||
| for.end: ; preds = %test1.for.body, %entry | ||
| ret void | ||
| } | ||
|
|
||
|
|
||
| ; CHECK-LABEL: 'DDG' for loop 'test2.for.body': | ||
| ; CHECK: Node Address:[[N1:0x[0-9a-f]*]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %i.02 = phi i64 [ %inc, %test2.for.body ], [ 0, %test2.for.body.preheader ] | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N2:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N3:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N4:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N5:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N5]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx = getelementptr inbounds float, float* %b, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N6:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N6]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %0 = load float, float* %arrayidx, align 4 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N7:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N4]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx1 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N8:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N8]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %1 = load float, float* %arrayidx1, align 4 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N7]] | ||
| ; CHECK-NEXT: [memory] to [[N9:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N7]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %add = fadd float %0, %1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N9]] | ||
|
|
||
| ; CHECK: Node Address:[[N3]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx2 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N9]] | ||
|
|
||
| ; CHECK: Node Address:[[N9]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: store float %add, float* %arrayidx2, align 4 | ||
| ; CHECK-NEXT: Edges:none! | ||
|
|
||
| ; CHECK: Node Address:[[N2]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %inc = add i64 %i.02, 1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N10:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N1]] | ||
|
|
||
| ; CHECK: Node Address:[[N10]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %exitcond = icmp ne i64 %inc, %n | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N11:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N11]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: br i1 %exitcond, label %test2.for.body, label %for.end.loopexit | ||
| ; CHECK-NEXT: Edges:none! | ||
|
|
||
| ;; Loop-independent memory dependencies. | ||
| ;; void test2(unsigned long n, float * restrict a, float * restrict b) { | ||
| ;; for (unsigned long i = 0; i < n; i++) | ||
| ;; a[i] = b[i] + a[i]; | ||
| ;; } | ||
|
|
||
| define void @test2(i64 %n, float* noalias %a, float* noalias %b) { | ||
| entry: | ||
| %exitcond1 = icmp ne i64 0, %n | ||
| br i1 %exitcond1, label %test2.for.body, label %for.end | ||
|
|
||
| test2.for.body: ; preds = %entry, %test2.for.body | ||
| %i.02 = phi i64 [ %inc, %test2.for.body ], [ 0, %entry ] | ||
| %arrayidx = getelementptr inbounds float, float* %b, i64 %i.02 | ||
| %0 = load float, float* %arrayidx, align 4 | ||
| %arrayidx1 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| %1 = load float, float* %arrayidx1, align 4 | ||
| %add = fadd float %0, %1 | ||
| %arrayidx2 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| store float %add, float* %arrayidx2, align 4 | ||
| %inc = add i64 %i.02, 1 | ||
| %exitcond = icmp ne i64 %inc, %n | ||
| br i1 %exitcond, label %test2.for.body, label %for.end | ||
|
|
||
| for.end: ; preds = %test2.for.body, %entry | ||
| ret void | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,216 @@ | ||
| ; RUN: opt < %s -disable-output "-passes=print<ddg>" 2>&1 | FileCheck %s | ||
|
|
||
| ; CHECK-LABEL: 'DDG' for loop 'test1.for.body': | ||
| ; CHECK: Node Address:[[N1:0x[0-9a-f]*]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %i.02 = phi i64 [ %inc, %test1.for.body ], [ 1, %test1.for.body.preheader ] | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N2:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N3:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N4:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N5:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N5]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx = getelementptr inbounds float, float* %b, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N6:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N6]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %0 = load float, float* %arrayidx, align 4 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N7:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N4]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %sub1 = add i64 %i.02, -1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N8:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N8]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx2 = getelementptr inbounds float, float* %a, i64 %sub1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N9:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N9]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %1 = load float, float* %arrayidx2, align 4 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N7]] | ||
|
|
||
| ; CHECK: Node Address:[[N7]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %add = fadd float %0, %1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N10:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N3]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx3 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N10]] | ||
|
|
||
| ; CHECK: Node Address:[[N10]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: store float %add, float* %arrayidx3, align 4 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [memory] to [[N9]] | ||
|
|
||
| ; CHECK: Node Address:[[N2]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %inc = add i64 %i.02, 1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N11:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N1]] | ||
|
|
||
| ; CHECK: Node Address:[[N11]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %cmp = icmp ult i64 %inc, %sub | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N12:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N12]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: br i1 %cmp, label %test1.for.body, label %for.end.loopexit | ||
| ; CHECK-NEXT: Edges:none! | ||
|
|
||
| ;; Loop-carried dependence requiring edge-reversal to expose a cycle | ||
| ;; in the graph. | ||
| ;; void test(unsigned long n, float * restrict a, float * restrict b) { | ||
| ;; for (unsigned long i = 1; i < n-1; i++) | ||
| ;; a[i] = b[i] + a[i-1]; | ||
| ;; } | ||
|
|
||
| define void @test1(i64 %n, float* noalias %a, float* noalias %b) { | ||
| entry: | ||
| %sub = add i64 %n, -1 | ||
| %cmp1 = icmp ult i64 1, %sub | ||
| br i1 %cmp1, label %test1.for.body, label %for.end | ||
|
|
||
| test1.for.body: ; preds = %entry, %test1.for.body | ||
| %i.02 = phi i64 [ %inc, %test1.for.body ], [ 1, %entry ] | ||
| %arrayidx = getelementptr inbounds float, float* %b, i64 %i.02 | ||
| %0 = load float, float* %arrayidx, align 4 | ||
| %sub1 = add i64 %i.02, -1 | ||
| %arrayidx2 = getelementptr inbounds float, float* %a, i64 %sub1 | ||
| %1 = load float, float* %arrayidx2, align 4 | ||
| %add = fadd float %0, %1 | ||
| %arrayidx3 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| store float %add, float* %arrayidx3, align 4 | ||
| %inc = add i64 %i.02, 1 | ||
| %cmp = icmp ult i64 %inc, %sub | ||
| br i1 %cmp, label %test1.for.body, label %for.end | ||
|
|
||
| for.end: ; preds = %test1.for.body, %entry | ||
| ret void | ||
| } | ||
|
|
||
|
|
||
| ; CHECK-LABEL: 'DDG' for loop 'test2.for.body': | ||
| ; CHECK: Node Address:[[N1:0x[0-9a-f]*]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %i.02 = phi i64 [ %inc, %test2.for.body ], [ 1, %test2.for.body.preheader ] | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N2:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N3:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N4:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N5:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N5]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx = getelementptr inbounds float, float* %b, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N6:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N6]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %0 = load float, float* %arrayidx, align 4 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N7:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N4]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %add1 = add i64 %i.02, 1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N8:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N8]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx2 = getelementptr inbounds float, float* %a, i64 %add1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N9:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N9]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %1 = load float, float* %arrayidx2, align 4 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N7]] | ||
| ; CHECK-NEXT: [memory] to [[N10:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N7]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %add = fadd float %0, %1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N10]] | ||
|
|
||
| ; CHECK: Node Address:[[N3]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %arrayidx3 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N10]] | ||
|
|
||
| ; CHECK: Node Address:[[N10]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: store float %add, float* %arrayidx3, align 4 | ||
| ; CHECK-NEXT: Edges:none! | ||
|
|
||
| ; CHECK: Node Address:[[N2]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %inc = add i64 %i.02, 1 | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N11:0x[0-9a-f]*]] | ||
| ; CHECK-NEXT: [def-use] to [[N1]] | ||
|
|
||
| ; CHECK: Node Address:[[N11]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: %cmp = icmp ult i64 %inc, %sub | ||
| ; CHECK-NEXT: Edges: | ||
| ; CHECK-NEXT: [def-use] to [[N12:0x[0-9a-f]*]] | ||
|
|
||
| ; CHECK: Node Address:[[N12]]:single-instruction | ||
| ; CHECK-NEXT: Instructions: | ||
| ; CHECK-NEXT: br i1 %cmp, label %test2.for.body, label %for.end.loopexit | ||
| ; CHECK-NEXT: Edges:none! | ||
|
|
||
|
|
||
| ;; Forward loop-carried dependence *not* causing a cycle. | ||
| ;; void test2(unsigned long n, float * restrict a, float * restrict b) { | ||
| ;; for (unsigned long i = 1; i < n-1; i++) | ||
| ;; a[i] = b[i] + a[i+1]; | ||
| ;; } | ||
|
|
||
| define void @test2(i64 %n, float* noalias %a, float* noalias %b) { | ||
| entry: | ||
| %sub = add i64 %n, -1 | ||
| %cmp1 = icmp ult i64 1, %sub | ||
| br i1 %cmp1, label %test2.for.body, label %for.end | ||
|
|
||
| test2.for.body: ; preds = %entry, %test2.for.body | ||
| %i.02 = phi i64 [ %inc, %test2.for.body ], [ 1, %entry ] | ||
| %arrayidx = getelementptr inbounds float, float* %b, i64 %i.02 | ||
| %0 = load float, float* %arrayidx, align 4 | ||
| %add1 = add i64 %i.02, 1 | ||
| %arrayidx2 = getelementptr inbounds float, float* %a, i64 %add1 | ||
| %1 = load float, float* %arrayidx2, align 4 | ||
| %add = fadd float %0, %1 | ||
| %arrayidx3 = getelementptr inbounds float, float* %a, i64 %i.02 | ||
| store float %add, float* %arrayidx3, align 4 | ||
| %inc = add i64 %i.02, 1 | ||
| %cmp = icmp ult i64 %inc, %sub | ||
| br i1 %cmp, label %test2.for.body, label %for.end | ||
|
|
||
| for.end: ; preds = %test2.for.body, %entry | ||
| ret void | ||
| } |