372 changes: 372 additions & 0 deletions llvm/include/llvm/Analysis/DDG.h
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
108 changes: 108 additions & 0 deletions llvm/include/llvm/Analysis/DependenceGraphBuilder.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
2 changes: 2 additions & 0 deletions llvm/lib/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ add_llvm_library(LLVMAnalysis
CostModel.cpp
CodeMetrics.cpp
ConstantFolding.cpp
DDG.cpp
Delinearization.cpp
DemandedBits.cpp
DependenceAnalysis.cpp
DependenceGraphBuilder.cpp
DivergenceAnalysis.cpp
DomPrinter.cpp
DomTreeUpdater.cpp
Expand Down
181 changes: 181 additions & 0 deletions llvm/lib/Analysis/DDG.cpp
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();
}
200 changes: 200 additions & 0 deletions llvm/lib/Analysis/DependenceGraphBuilder.cpp
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>;
1 change: 1 addition & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "llvm/Analysis/CFLSteensAliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Analysis/DDG.h"
#include "llvm/Analysis/DemandedBits.h"
#include "llvm/Analysis/DependenceAnalysis.h"
#include "llvm/Analysis/DominanceFrontier.h"
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ FUNCTION_PASS_WITH_PARAMS("mldst-motion",
#endif
LOOP_ANALYSIS("no-op-loop", NoOpLoopAnalysis())
LOOP_ANALYSIS("access-info", LoopAccessAnalysis())
LOOP_ANALYSIS("ddg", DDGAnalysis())
LOOP_ANALYSIS("ivusers", IVUsersAnalysis())
LOOP_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC))
#undef LOOP_ANALYSIS
Expand All @@ -303,6 +304,7 @@ LOOP_PASS("irce", IRCEPass())
LOOP_PASS("unroll-and-jam", LoopUnrollAndJamPass())
LOOP_PASS("unroll-full", LoopFullUnrollPass())
LOOP_PASS("print-access-info", LoopAccessInfoPrinterPass(dbgs()))
LOOP_PASS("print<ddg>", DDGAnalysisPrinterPass(dbgs()))
LOOP_PASS("print<ivusers>", IVUsersPrinterPass(dbgs()))
LOOP_PASS("print<loop-cache-cost>", LoopCachePrinterPass(dbgs()))
LOOP_PASS("loop-predication", LoopPredicationPass())
Expand Down
189 changes: 189 additions & 0 deletions llvm/test/Analysis/DDG/basic-a.ll
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
}
216 changes: 216 additions & 0 deletions llvm/test/Analysis/DDG/basic-b.ll
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
}
434 changes: 434 additions & 0 deletions llvm/test/Analysis/DDG/basic-loopnest.ll

Large diffs are not rendered by default.