Skip to content

Commit 91b62d5

Browse files
committed
[DDG] Data Dependence Graph - Root Node
Summary: This patch adds Root Node to the DDG. The purpose of the root node is to create a single entry node that allows graph walk iterators to iterate through all nodes of the graph, making sure that no node is left unvisited during a graph walk (eg. SCC or DFS). Once the DDG is fully constructed it will have exactly one root node. Every node in the graph is reachable from the root. The algorithm for connecting the root node is based on depth-first-search that keeps track of visited nodes to try to avoid creating unnecessary edges. Authored By: bmahjour Reviewer: Meinersbur, fhahn, myhsu, xtian, dmgreen, kbarton, jdoerfert Reviewed By: Meinersbur Subscribers: ychen, arphaman, simoll, a.elovikov, mgorny, hiraditya, jfb, wuzish, llvm-commits, jsji, Whitney, etiotto, ppc-slack Tag: #llvm Differential Revision: https://reviews.llvm.org/D67970 llvm-svn: 373386
1 parent bcab951 commit 91b62d5

File tree

5 files changed

+177
-6
lines changed

5 files changed

+177
-6
lines changed

llvm/include/llvm/Analysis/DDG.h

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class LPMUpdater;
3333
/// 1. Single instruction node containing just one instruction.
3434
/// 2. Multiple instruction node where two or more instructions from
3535
/// the same basic block are merged into one node.
36+
/// 3. Root node is a special node that connects to all components such that
37+
/// there is always a path from it to any node in the graph.
3638
class DDGNode : public DDGNodeBase {
3739
public:
3840
using InstructionListType = SmallVectorImpl<Instruction *>;
@@ -41,6 +43,7 @@ class DDGNode : public DDGNodeBase {
4143
Unknown,
4244
SingleInstruction,
4345
MultiInstruction,
46+
Root,
4447
};
4548

4649
DDGNode() = delete;
@@ -78,6 +81,22 @@ class DDGNode : public DDGNodeBase {
7881
NodeKind Kind;
7982
};
8083

84+
/// Subclass of DDGNode representing the root node of the graph.
85+
/// There should only be one such node in a given graph.
86+
class RootDDGNode : public DDGNode {
87+
public:
88+
RootDDGNode() : DDGNode(NodeKind::Root) {}
89+
RootDDGNode(const RootDDGNode &N) = delete;
90+
RootDDGNode(RootDDGNode &&N) : DDGNode(std::move(N)) {}
91+
~RootDDGNode() {}
92+
93+
/// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
94+
static bool classof(const DDGNode *N) {
95+
return N->getKind() == NodeKind::Root;
96+
}
97+
static bool classof(const RootDDGNode *N) { return true; }
98+
};
99+
81100
/// Subclass of DDGNode representing single or multi-instruction nodes.
82101
class SimpleDDGNode : public DDGNode {
83102
public:
@@ -139,10 +158,12 @@ class SimpleDDGNode : public DDGNode {
139158
/// Data Dependency Graph Edge.
140159
/// An edge in the DDG can represent a def-use relationship or
141160
/// a memory dependence based on the result of DependenceAnalysis.
161+
/// A rooted edge connects the root node to one of the components
162+
/// of the graph.
142163
class DDGEdge : public DDGEdgeBase {
143164
public:
144165
/// The kind of edge in the DDG
145-
enum class EdgeKind { Unknown, RegisterDefUse, MemoryDependence };
166+
enum class EdgeKind { Unknown, RegisterDefUse, MemoryDependence, Rooted };
146167

147168
explicit DDGEdge(DDGNode &N) = delete;
148169
DDGEdge(DDGNode &N, EdgeKind K) : DDGEdgeBase(N), Kind(K) {}
@@ -169,6 +190,10 @@ class DDGEdge : public DDGEdgeBase {
169190
/// Return true if this is a memory dependence edge, and false otherwise.
170191
bool isMemoryDependence() const { return Kind == EdgeKind::MemoryDependence; }
171192

193+
/// Return true if this is an edge stemming from the root node, and false
194+
/// otherwise.
195+
bool isRooted() const { return Kind == EdgeKind::Rooted; }
196+
172197
private:
173198
EdgeKind Kind;
174199
};
@@ -182,14 +207,21 @@ template <typename NodeType> class DependenceGraphInfo {
182207
DependenceGraphInfo() = delete;
183208
DependenceGraphInfo(const DependenceGraphInfo &G) = delete;
184209
DependenceGraphInfo(const std::string &N, const DependenceInfo &DepInfo)
185-
: Name(N), DI(DepInfo) {}
210+
: Name(N), DI(DepInfo), Root(nullptr) {}
186211
DependenceGraphInfo(DependenceGraphInfo &&G)
187-
: Name(std::move(G.Name)), DI(std::move(G.DI)) {}
212+
: Name(std::move(G.Name)), DI(std::move(G.DI)), Root(G.Root) {}
188213
virtual ~DependenceGraphInfo() {}
189214

190215
/// Return the label that is used to name this graph.
191216
const StringRef getName() const { return Name; }
192217

218+
/// Return the root node of the graph.
219+
NodeType &getRoot() const {
220+
assert(Root && "Root node is not available yet. Graph construction may "
221+
"still be in progress\n");
222+
return *Root;
223+
}
224+
193225
protected:
194226
// Name of the graph.
195227
std::string Name;
@@ -198,6 +230,10 @@ template <typename NodeType> class DependenceGraphInfo {
198230
// dependencies don't need to be stored. Instead when the dependence is
199231
// queried it is recomputed using @DI.
200232
const DependenceInfo DI;
233+
234+
// A special node in the graph that has an edge to every connected component of
235+
// the graph, to ensure all nodes are reachable in a graph walk.
236+
NodeType *Root = nullptr;
201237
};
202238

203239
using DDGInfo = DependenceGraphInfo<DDGNode>;
@@ -217,6 +253,12 @@ class DataDependenceGraph : public DDGBase, public DDGInfo {
217253
DataDependenceGraph(Function &F, DependenceInfo &DI);
218254
DataDependenceGraph(const Loop &L, DependenceInfo &DI);
219255
~DataDependenceGraph();
256+
257+
protected:
258+
/// Add node \p N to the graph, if it's not added yet, and keep track of
259+
/// the root node. Return true if node is successfully added.
260+
bool addNode(NodeType &N);
261+
220262
};
221263

222264
/// Concrete implementation of a pure data dependence graph builder. This class
@@ -230,6 +272,12 @@ class DDGBuilder : public AbstractDependenceGraphBuilder<DataDependenceGraph> {
230272
DDGBuilder(DataDependenceGraph &G, DependenceInfo &D,
231273
const BasicBlockListType &BBs)
232274
: AbstractDependenceGraphBuilder(G, D, BBs) {}
275+
DDGNode &createRootNode() final override {
276+
auto *RN = new RootDDGNode();
277+
assert(RN && "Failed to allocate memory for DDG root node.");
278+
Graph.addNode(*RN);
279+
return *RN;
280+
}
233281
DDGNode &createFineGrainedNode(Instruction &I) final override {
234282
auto *SN = new SimpleDDGNode(I);
235283
assert(SN && "Failed to allocate memory for simple DDG node.");
@@ -248,6 +296,14 @@ class DDGBuilder : public AbstractDependenceGraphBuilder<DataDependenceGraph> {
248296
Graph.connect(Src, Tgt, *E);
249297
return *E;
250298
}
299+
DDGEdge &createRootedEdge(DDGNode &Src, DDGNode &Tgt) final override {
300+
auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::Rooted);
301+
assert(E && "Failed to allocate memory for edge");
302+
assert(isa<RootDDGNode>(Src) && "Expected root node");
303+
Graph.connect(Src, Tgt, *E);
304+
return *E;
305+
}
306+
251307
};
252308

253309
raw_ostream &operator<<(raw_ostream &OS, const DDGNode &N);
@@ -317,7 +373,9 @@ template <> struct GraphTraits<DDGNode *> {
317373
template <>
318374
struct GraphTraits<DataDependenceGraph *> : public GraphTraits<DDGNode *> {
319375
using nodes_iterator = DataDependenceGraph::iterator;
320-
static NodeRef getEntryNode(DataDependenceGraph *DG) { return *DG->begin(); }
376+
static NodeRef getEntryNode(DataDependenceGraph *DG) {
377+
return &DG->getRoot();
378+
}
321379
static nodes_iterator nodes_begin(DataDependenceGraph *DG) {
322380
return DG->begin();
323381
}
@@ -357,7 +415,7 @@ struct GraphTraits<const DataDependenceGraph *>
357415
: public GraphTraits<const DDGNode *> {
358416
using nodes_iterator = DataDependenceGraph::const_iterator;
359417
static NodeRef getEntryNode(const DataDependenceGraph *DG) {
360-
return *DG->begin();
418+
return &DG->getRoot();
361419
}
362420
static nodes_iterator nodes_begin(const DataDependenceGraph *DG) {
363421
return DG->begin();

llvm/include/llvm/Analysis/DependenceGraphBuilder.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ template <class GraphType> class AbstractDependenceGraphBuilder {
5555
createFineGrainedNodes();
5656
createDefUseEdges();
5757
createMemoryDependencyEdges();
58+
createAndConnectRootNode();
5859
}
5960

6061
/// Create fine grained nodes. These are typically atomic nodes that
@@ -69,7 +70,14 @@ template <class GraphType> class AbstractDependenceGraphBuilder {
6970
/// in the graph nodes and create edges between them.
7071
void createMemoryDependencyEdges();
7172

73+
/// Create a root node and add edges such that each node in the graph is
74+
/// reachable from the root.
75+
void createAndConnectRootNode();
76+
7277
protected:
78+
/// Create the root node of the graph.
79+
virtual NodeType &createRootNode() = 0;
80+
7381
/// Create an atomic node in the graph given a single instruction.
7482
virtual NodeType &createFineGrainedNode(Instruction &I) = 0;
7583

@@ -79,6 +87,9 @@ template <class GraphType> class AbstractDependenceGraphBuilder {
7987
/// Create a memory dependence edge going from \p Src to \p Tgt.
8088
virtual EdgeType &createMemoryEdge(NodeType &Src, NodeType &Tgt) = 0;
8189

90+
/// Create a rooted edge going from \p Src to \p Tgt .
91+
virtual EdgeType &createRootedEdge(NodeType &Src, NodeType &Tgt) = 0;
92+
8293
/// Deallocate memory of edge \p E.
8394
virtual void destroyEdge(EdgeType &E) { delete &E; }
8495

llvm/lib/Analysis/DDG.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGNode::NodeKind K) {
4646
case DDGNode::NodeKind::MultiInstruction:
4747
Out = "multi-instruction";
4848
break;
49+
case DDGNode::NodeKind::Root:
50+
Out = "root";
51+
break;
4952
case DDGNode::NodeKind::Unknown:
5053
Out = "??";
5154
break;
@@ -60,7 +63,7 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGNode &N) {
6063
OS << " Instructions:\n";
6164
for (auto *I : cast<const SimpleDDGNode>(N).getInstructions())
6265
OS.indent(2) << *I << "\n";
63-
} else
66+
} else if (!isa<RootDDGNode>(N))
6467
llvm_unreachable("unimplemented type of node");
6568

6669
OS << (N.getEdges().empty() ? " Edges:none!\n" : " Edges:\n");
@@ -108,6 +111,9 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGEdge::EdgeKind K) {
108111
case DDGEdge::EdgeKind::MemoryDependence:
109112
Out = "memory";
110113
break;
114+
case DDGEdge::EdgeKind::Rooted:
115+
Out = "rooted";
116+
break;
111117
case DDGEdge::EdgeKind::Unknown:
112118
Out = "??";
113119
break;
@@ -153,6 +159,22 @@ DataDependenceGraph::~DataDependenceGraph() {
153159
}
154160
}
155161

162+
bool DataDependenceGraph::addNode(DDGNode &N) {
163+
if (!DDGBase::addNode(N))
164+
return false;
165+
166+
// In general, if the root node is already created and linked, it is not safe
167+
// to add new nodes since they may be unreachable by the root.
168+
// TODO: Allow adding Pi-block nodes after root is created. Pi-blocks are an
169+
// exception because they represent components that are already reachable by
170+
// root.
171+
assert(!Root && "Root node is already added. No more nodes can be added.");
172+
if (isa<RootDDGNode>(N))
173+
Root = &N;
174+
175+
return true;
176+
}
177+
156178
raw_ostream &llvm::operator<<(raw_ostream &OS, const DataDependenceGraph &G) {
157179
for (auto *Node : G)
158180
OS << *Node << "\n";

llvm/lib/Analysis/DependenceGraphBuilder.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,34 @@ void AbstractDependenceGraphBuilder<G>::createFineGrainedNodes() {
4646
}
4747
}
4848

49+
template <class G>
50+
void AbstractDependenceGraphBuilder<G>::createAndConnectRootNode() {
51+
// Create a root node that connects to every connected component of the graph.
52+
// This is done to allow graph iterators to visit all the disjoint components
53+
// of the graph, in a single walk.
54+
//
55+
// This algorithm works by going through each node of the graph and for each
56+
// node N, do a DFS starting from N. A rooted edge is established between the
57+
// root node and N (if N is not yet visited). All the nodes reachable from N
58+
// are marked as visited and are skipped in the DFS of subsequent nodes.
59+
//
60+
// Note: This algorithm tries to limit the number of edges out of the root
61+
// node to some extent, but there may be redundant edges created depending on
62+
// the iteration order. For example for a graph {A -> B}, an edge from the
63+
// root node is added to both nodes if B is visited before A. While it does
64+
// not result in minimal number of edges, this approach saves compile-time
65+
// while keeping the number of edges in check.
66+
auto &RootNode = createRootNode();
67+
df_iterator_default_set<const NodeType *, 4> Visited;
68+
for (auto *N : Graph) {
69+
if (*N == RootNode)
70+
continue;
71+
for (auto I : depth_first_ext(N, Visited))
72+
if (I == N)
73+
createRootedEdge(RootNode, *N);
74+
}
75+
}
76+
4977
template <class G> void AbstractDependenceGraphBuilder<G>::createDefUseEdges() {
5078
for (NodeType *N : Graph) {
5179
InstructionListType SrcIList;

llvm/test/Analysis/DDG/root-node.ll

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
; RUN: opt < %s -disable-output "-passes=print<ddg>" 2>&1 | FileCheck %s
2+
3+
; CHECK-LABEL: 'DDG' for loop 'test1.for.body':
4+
5+
; CHECK: Node Address:[[N1:0x[0-9a-f]*]]:single-instruction
6+
; CHECK-NEXT: Instructions:
7+
; CHECK-NEXT: %i2.03 = phi i64 [ 0, %for.body.lr.ph ], [ %inc2, %test1.for.body ]
8+
9+
; CHECK: Node Address:[[N2:0x[0-9a-f]*]]:single-instruction
10+
; CHECK-NEXT: Instructions:
11+
; CHECK-NEXT: %i1.02 = phi i64 [ 0, %for.body.lr.ph ], [ %inc, %test1.for.body ]
12+
13+
; CHECK: Node Address:[[ROOT:0x[0-9a-f]*]]:root
14+
; CHECK-NEXT: Edges:
15+
; CHECK-NEXT: [rooted] to [[N1]]
16+
; CHECK-NEXT: [rooted] to [[N2]]
17+
18+
19+
;; // Two separate components in the graph. Root node must link to both.
20+
;; void test1(unsigned long n, float * restrict a, float * restrict b) {
21+
;; for (unsigned long i1 = 0, i2 = 0; i1 < n; i1++, i2++) {
22+
;; a[i1] = 1;
23+
;; b[i2] = -1;
24+
;; }
25+
;; }
26+
27+
define void @test1(i64 %n, float* noalias %a, float* noalias %b) {
28+
entry:
29+
%cmp1 = icmp ult i64 0, %n
30+
br i1 %cmp1, label %for.body.lr.ph, label %for.end
31+
32+
for.body.lr.ph: ; preds = %entry
33+
br label %test1.for.body
34+
35+
test1.for.body: ; preds = %for.body.lr.ph, %test1.for.body
36+
%i2.03 = phi i64 [ 0, %for.body.lr.ph ], [ %inc2, %test1.for.body ]
37+
%i1.02 = phi i64 [ 0, %for.body.lr.ph ], [ %inc, %test1.for.body ]
38+
%arrayidx = getelementptr inbounds float, float* %a, i64 %i1.02
39+
store float 1.000000e+00, float* %arrayidx, align 4
40+
%arrayidx1 = getelementptr inbounds float, float* %b, i64 %i2.03
41+
store float -1.000000e+00, float* %arrayidx1, align 4
42+
%inc = add i64 %i1.02, 1
43+
%inc2 = add i64 %i2.03, 1
44+
%cmp = icmp ult i64 %inc, %n
45+
br i1 %cmp, label %test1.for.body, label %for.cond.for.end_crit_edge
46+
47+
for.cond.for.end_crit_edge: ; preds = %test1.for.body
48+
br label %for.end
49+
50+
for.end: ; preds = %for.cond.for.end_crit_edge, %entry
51+
ret void
52+
}

0 commit comments

Comments
 (0)