Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14 pmbaty 1
//===- llvm/Analysis/DDG.h --------------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file defines the Data-Dependence Graph (DDG).
10
//
11
//===----------------------------------------------------------------------===//
12
 
13
#ifndef LLVM_ANALYSIS_DDG_H
14
#define LLVM_ANALYSIS_DDG_H
15
 
16
#include "llvm/ADT/DenseMap.h"
17
#include "llvm/ADT/DirectedGraph.h"
18
#include "llvm/Analysis/DependenceAnalysis.h"
19
#include "llvm/Analysis/DependenceGraphBuilder.h"
20
#include "llvm/Analysis/LoopAnalysisManager.h"
21
 
22
namespace llvm {
23
class Function;
24
class Loop;
25
class LoopInfo;
26
class DDGNode;
27
class DDGEdge;
28
using DDGNodeBase = DGNode<DDGNode, DDGEdge>;
29
using DDGEdgeBase = DGEdge<DDGNode, DDGEdge>;
30
using DDGBase = DirectedGraph<DDGNode, DDGEdge>;
31
class LPMUpdater;
32
 
33
/// Data Dependence Graph Node
34
/// The graph can represent the following types of nodes:
35
/// 1. Single instruction node containing just one instruction.
36
/// 2. Multiple instruction node where two or more instructions from
37
///    the same basic block are merged into one node.
38
/// 3. Pi-block node which is a group of other DDG nodes that are part of a
39
///    strongly-connected component of the graph.
40
///    A pi-block node contains more than one single or multiple instruction
41
///    nodes. The root node cannot be part of a pi-block.
42
/// 4. Root node is a special node that connects to all components such that
43
///    there is always a path from it to any node in the graph.
44
class DDGNode : public DDGNodeBase {
45
public:
46
  using InstructionListType = SmallVectorImpl<Instruction *>;
47
 
48
  enum class NodeKind {
49
    Unknown,
50
    SingleInstruction,
51
    MultiInstruction,
52
    PiBlock,
53
    Root,
54
  };
55
 
56
  DDGNode() = delete;
57
  DDGNode(const NodeKind K) : Kind(K) {}
58
  DDGNode(const DDGNode &N) = default;
59
  DDGNode(DDGNode &&N) : DDGNodeBase(std::move(N)), Kind(N.Kind) {}
60
  virtual ~DDGNode() = 0;
61
 
62
  DDGNode &operator=(const DDGNode &N) {
63
    DGNode::operator=(N);
64
    Kind = N.Kind;
65
    return *this;
66
  }
67
 
68
  DDGNode &operator=(DDGNode &&N) {
69
    DGNode::operator=(std::move(N));
70
    Kind = N.Kind;
71
    return *this;
72
  }
73
 
74
  /// Getter for the kind of this node.
75
  NodeKind getKind() const { return Kind; }
76
 
77
  /// Collect a list of instructions, in \p IList, for which predicate \p Pred
78
  /// evaluates to true when iterating over instructions of this node. Return
79
  /// true if at least one instruction was collected, and false otherwise.
80
  bool collectInstructions(llvm::function_ref<bool(Instruction *)> const &Pred,
81
                           InstructionListType &IList) const;
82
 
83
protected:
84
  /// Setter for the kind of this node.
85
  void setKind(NodeKind K) { Kind = K; }
86
 
87
private:
88
  NodeKind Kind;
89
};
90
 
91
/// Subclass of DDGNode representing the root node of the graph.
92
/// There should only be one such node in a given graph.
93
class RootDDGNode : public DDGNode {
94
public:
95
  RootDDGNode() : DDGNode(NodeKind::Root) {}
96
  RootDDGNode(const RootDDGNode &N) = delete;
97
  RootDDGNode(RootDDGNode &&N) : DDGNode(std::move(N)) {}
98
  ~RootDDGNode() = default;
99
 
100
  /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
101
  static bool classof(const DDGNode *N) {
102
    return N->getKind() == NodeKind::Root;
103
  }
104
  static bool classof(const RootDDGNode *N) { return true; }
105
};
106
 
107
/// Subclass of DDGNode representing single or multi-instruction nodes.
108
class SimpleDDGNode : public DDGNode {
109
  friend class DDGBuilder;
110
 
111
public:
112
  SimpleDDGNode() = delete;
113
  SimpleDDGNode(Instruction &I);
114
  SimpleDDGNode(const SimpleDDGNode &N);
115
  SimpleDDGNode(SimpleDDGNode &&N);
116
  ~SimpleDDGNode();
117
 
118
  SimpleDDGNode &operator=(const SimpleDDGNode &N) = default;
119
 
120
  SimpleDDGNode &operator=(SimpleDDGNode &&N) {
121
    DDGNode::operator=(std::move(N));
122
    InstList = std::move(N.InstList);
123
    return *this;
124
  }
125
 
126
  /// Get the list of instructions in this node.
127
  const InstructionListType &getInstructions() const {
128
    assert(!InstList.empty() && "Instruction List is empty.");
129
    return InstList;
130
  }
131
  InstructionListType &getInstructions() {
132
    return const_cast<InstructionListType &>(
133
        static_cast<const SimpleDDGNode *>(this)->getInstructions());
134
  }
135
 
136
  /// Get the first/last instruction in the node.
137
  Instruction *getFirstInstruction() const { return getInstructions().front(); }
138
  Instruction *getLastInstruction() const { return getInstructions().back(); }
139
 
140
  /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
141
  static bool classof(const DDGNode *N) {
142
    return N->getKind() == NodeKind::SingleInstruction ||
143
           N->getKind() == NodeKind::MultiInstruction;
144
  }
145
  static bool classof(const SimpleDDGNode *N) { return true; }
146
 
147
private:
148
  /// Append the list of instructions in \p Input to this node.
149
  void appendInstructions(const InstructionListType &Input) {
150
    setKind((InstList.size() == 0 && Input.size() == 1)
151
                ? NodeKind::SingleInstruction
152
                : NodeKind::MultiInstruction);
153
    llvm::append_range(InstList, Input);
154
  }
155
  void appendInstructions(const SimpleDDGNode &Input) {
156
    appendInstructions(Input.getInstructions());
157
  }
158
 
159
  /// List of instructions associated with a single or multi-instruction node.
160
  SmallVector<Instruction *, 2> InstList;
161
};
162
 
163
/// Subclass of DDGNode representing a pi-block. A pi-block represents a group
164
/// of DDG nodes that are part of a strongly-connected component of the graph.
165
/// Replacing all the SCCs with pi-blocks results in an acyclic representation
166
/// of the DDG. For example if we have:
167
/// {a -> b}, {b -> c, d}, {c -> a}
168
/// the cycle a -> b -> c -> a is abstracted into a pi-block "p" as follows:
169
/// {p -> d} with "p" containing: {a -> b}, {b -> c}, {c -> a}
170
class PiBlockDDGNode : public DDGNode {
171
public:
172
  using PiNodeList = SmallVector<DDGNode *, 4>;
173
 
174
  PiBlockDDGNode() = delete;
175
  PiBlockDDGNode(const PiNodeList &List);
176
  PiBlockDDGNode(const PiBlockDDGNode &N);
177
  PiBlockDDGNode(PiBlockDDGNode &&N);
178
  ~PiBlockDDGNode();
179
 
180
  PiBlockDDGNode &operator=(const PiBlockDDGNode &N) = default;
181
 
182
  PiBlockDDGNode &operator=(PiBlockDDGNode &&N) {
183
    DDGNode::operator=(std::move(N));
184
    NodeList = std::move(N.NodeList);
185
    return *this;
186
  }
187
 
188
  /// Get the list of nodes in this pi-block.
189
  const PiNodeList &getNodes() const {
190
    assert(!NodeList.empty() && "Node list is empty.");
191
    return NodeList;
192
  }
193
  PiNodeList &getNodes() {
194
    return const_cast<PiNodeList &>(
195
        static_cast<const PiBlockDDGNode *>(this)->getNodes());
196
  }
197
 
198
  /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
199
  static bool classof(const DDGNode *N) {
200
    return N->getKind() == NodeKind::PiBlock;
201
  }
202
 
203
private:
204
  /// List of nodes in this pi-block.
205
  PiNodeList NodeList;
206
};
207
 
208
/// Data Dependency Graph Edge.
209
/// An edge in the DDG can represent a def-use relationship or
210
/// a memory dependence based on the result of DependenceAnalysis.
211
/// A rooted edge connects the root node to one of the components
212
/// of the graph.
213
class DDGEdge : public DDGEdgeBase {
214
public:
215
  /// The kind of edge in the DDG
216
  enum class EdgeKind {
217
    Unknown,
218
    RegisterDefUse,
219
    MemoryDependence,
220
    Rooted,
221
    Last = Rooted // Must be equal to the largest enum value.
222
  };
223
 
224
  explicit DDGEdge(DDGNode &N) = delete;
225
  DDGEdge(DDGNode &N, EdgeKind K) : DDGEdgeBase(N), Kind(K) {}
226
  DDGEdge(const DDGEdge &E) : DDGEdgeBase(E), Kind(E.getKind()) {}
227
  DDGEdge(DDGEdge &&E) : DDGEdgeBase(std::move(E)), Kind(E.Kind) {}
228
  DDGEdge &operator=(const DDGEdge &E) = default;
229
 
230
  DDGEdge &operator=(DDGEdge &&E) {
231
    DDGEdgeBase::operator=(std::move(E));
232
    Kind = E.Kind;
233
    return *this;
234
  }
235
 
236
  /// Get the edge kind
237
  EdgeKind getKind() const { return Kind; };
238
 
239
  /// Return true if this is a def-use edge, and false otherwise.
240
  bool isDefUse() const { return Kind == EdgeKind::RegisterDefUse; }
241
 
242
  /// Return true if this is a memory dependence edge, and false otherwise.
243
  bool isMemoryDependence() const { return Kind == EdgeKind::MemoryDependence; }
244
 
245
  /// Return true if this is an edge stemming from the root node, and false
246
  /// otherwise.
247
  bool isRooted() const { return Kind == EdgeKind::Rooted; }
248
 
249
private:
250
  EdgeKind Kind;
251
};
252
 
253
/// Encapsulate some common data and functionality needed for different
254
/// variations of data dependence graphs.
255
template <typename NodeType> class DependenceGraphInfo {
256
public:
257
  using DependenceList = SmallVector<std::unique_ptr<Dependence>, 1>;
258
 
259
  DependenceGraphInfo() = delete;
260
  DependenceGraphInfo(const DependenceGraphInfo &G) = delete;
261
  DependenceGraphInfo(const std::string &N, const DependenceInfo &DepInfo)
262
      : Name(N), DI(DepInfo), Root(nullptr) {}
263
  DependenceGraphInfo(DependenceGraphInfo &&G)
264
      : Name(std::move(G.Name)), DI(std::move(G.DI)), Root(G.Root) {}
265
  virtual ~DependenceGraphInfo() = default;
266
 
267
  /// Return the label that is used to name this graph.
268
  StringRef getName() const { return Name; }
269
 
270
  /// Return the root node of the graph.
271
  NodeType &getRoot() const {
272
    assert(Root && "Root node is not available yet. Graph construction may "
273
                   "still be in progress\n");
274
    return *Root;
275
  }
276
 
277
  /// Collect all the data dependency infos coming from any pair of memory
278
  /// accesses from \p Src to \p Dst, and store them into \p Deps. Return true
279
  /// if a dependence exists, and false otherwise.
280
  bool getDependencies(const NodeType &Src, const NodeType &Dst,
281
                       DependenceList &Deps) const;
282
 
283
  /// Return a string representing the type of dependence that the dependence
284
  /// analysis identified between the two given nodes. This function assumes
285
  /// that there is a memory dependence between the given two nodes.
286
  std::string getDependenceString(const NodeType &Src,
287
                                  const NodeType &Dst) const;
288
 
289
protected:
290
  // Name of the graph.
291
  std::string Name;
292
 
293
  // Store a copy of DependenceInfo in the graph, so that individual memory
294
  // dependencies don't need to be stored. Instead when the dependence is
295
  // queried it is recomputed using @DI.
296
  const DependenceInfo DI;
297
 
298
  // A special node in the graph that has an edge to every connected component of
299
  // the graph, to ensure all nodes are reachable in a graph walk.
300
  NodeType *Root = nullptr;
301
};
302
 
303
using DDGInfo = DependenceGraphInfo<DDGNode>;
304
 
305
/// Data Dependency Graph
306
class DataDependenceGraph : public DDGBase, public DDGInfo {
307
  friend AbstractDependenceGraphBuilder<DataDependenceGraph>;
308
  friend class DDGBuilder;
309
 
310
public:
311
  using NodeType = DDGNode;
312
  using EdgeType = DDGEdge;
313
 
314
  DataDependenceGraph() = delete;
315
  DataDependenceGraph(const DataDependenceGraph &G) = delete;
316
  DataDependenceGraph(DataDependenceGraph &&G)
317
      : DDGBase(std::move(G)), DDGInfo(std::move(G)) {}
318
  DataDependenceGraph(Function &F, DependenceInfo &DI);
319
  DataDependenceGraph(Loop &L, LoopInfo &LI, DependenceInfo &DI);
320
  ~DataDependenceGraph();
321
 
322
  /// If node \p N belongs to a pi-block return a pointer to the pi-block,
323
  /// otherwise return null.
324
  const PiBlockDDGNode *getPiBlock(const NodeType &N) const;
325
 
326
protected:
327
  /// Add node \p N to the graph, if it's not added yet, and keep track of the
328
  /// root node as well as pi-blocks and their members. Return true if node is
329
  /// successfully added.
330
  bool addNode(NodeType &N);
331
 
332
private:
333
  using PiBlockMapType = DenseMap<const NodeType *, const PiBlockDDGNode *>;
334
 
335
  /// Mapping from graph nodes to their containing pi-blocks. If a node is not
336
  /// part of a pi-block, it will not appear in this map.
337
  PiBlockMapType PiBlockMap;
338
};
339
 
340
/// Concrete implementation of a pure data dependence graph builder. This class
341
/// provides custom implementation for the pure-virtual functions used in the
342
/// generic dependence graph build algorithm.
343
///
344
/// For information about time complexity of the build algorithm see the
345
/// comments near the declaration of AbstractDependenceGraphBuilder.
346
class DDGBuilder : public AbstractDependenceGraphBuilder<DataDependenceGraph> {
347
public:
348
  DDGBuilder(DataDependenceGraph &G, DependenceInfo &D,
349
             const BasicBlockListType &BBs)
350
      : AbstractDependenceGraphBuilder(G, D, BBs) {}
351
  DDGNode &createRootNode() final {
352
    auto *RN = new RootDDGNode();
353
    assert(RN && "Failed to allocate memory for DDG root node.");
354
    Graph.addNode(*RN);
355
    return *RN;
356
  }
357
  DDGNode &createFineGrainedNode(Instruction &I) final {
358
    auto *SN = new SimpleDDGNode(I);
359
    assert(SN && "Failed to allocate memory for simple DDG node.");
360
    Graph.addNode(*SN);
361
    return *SN;
362
  }
363
  DDGNode &createPiBlock(const NodeListType &L) final {
364
    auto *Pi = new PiBlockDDGNode(L);
365
    assert(Pi && "Failed to allocate memory for pi-block node.");
366
    Graph.addNode(*Pi);
367
    return *Pi;
368
  }
369
  DDGEdge &createDefUseEdge(DDGNode &Src, DDGNode &Tgt) final {
370
    auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::RegisterDefUse);
371
    assert(E && "Failed to allocate memory for edge");
372
    Graph.connect(Src, Tgt, *E);
373
    return *E;
374
  }
375
  DDGEdge &createMemoryEdge(DDGNode &Src, DDGNode &Tgt) final {
376
    auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::MemoryDependence);
377
    assert(E && "Failed to allocate memory for edge");
378
    Graph.connect(Src, Tgt, *E);
379
    return *E;
380
  }
381
  DDGEdge &createRootedEdge(DDGNode &Src, DDGNode &Tgt) final {
382
    auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::Rooted);
383
    assert(E && "Failed to allocate memory for edge");
384
    assert(isa<RootDDGNode>(Src) && "Expected root node");
385
    Graph.connect(Src, Tgt, *E);
386
    return *E;
387
  }
388
 
389
  const NodeListType &getNodesInPiBlock(const DDGNode &N) final {
390
    auto *PiNode = dyn_cast<const PiBlockDDGNode>(&N);
391
    assert(PiNode && "Expected a pi-block node.");
392
    return PiNode->getNodes();
393
  }
394
 
395
  /// Return true if the two nodes \pSrc and \pTgt are both simple nodes and
396
  /// the consecutive instructions after merging belong to the same basic block.
397
  bool areNodesMergeable(const DDGNode &Src, const DDGNode &Tgt) const final;
398
  void mergeNodes(DDGNode &Src, DDGNode &Tgt) final;
399
  bool shouldSimplify() const final;
400
  bool shouldCreatePiBlocks() const final;
401
};
402
 
403
raw_ostream &operator<<(raw_ostream &OS, const DDGNode &N);
404
raw_ostream &operator<<(raw_ostream &OS, const DDGNode::NodeKind K);
405
raw_ostream &operator<<(raw_ostream &OS, const DDGEdge &E);
406
raw_ostream &operator<<(raw_ostream &OS, const DDGEdge::EdgeKind K);
407
raw_ostream &operator<<(raw_ostream &OS, const DataDependenceGraph &G);
408
 
409
//===--------------------------------------------------------------------===//
410
// DDG Analysis Passes
411
//===--------------------------------------------------------------------===//
412
 
413
/// Analysis pass that builds the DDG for a loop.
414
class DDGAnalysis : public AnalysisInfoMixin<DDGAnalysis> {
415
public:
416
  using Result = std::unique_ptr<DataDependenceGraph>;
417
  Result run(Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR);
418
 
419
private:
420
  friend AnalysisInfoMixin<DDGAnalysis>;
421
  static AnalysisKey Key;
422
};
423
 
424
/// Textual printer pass for the DDG of a loop.
425
class DDGAnalysisPrinterPass : public PassInfoMixin<DDGAnalysisPrinterPass> {
426
public:
427
  explicit DDGAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {}
428
  PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM,
429
                        LoopStandardAnalysisResults &AR, LPMUpdater &U);
430
 
431
private:
432
  raw_ostream &OS;
433
};
434
 
435
//===--------------------------------------------------------------------===//
436
// DependenceGraphInfo Implementation
437
//===--------------------------------------------------------------------===//
438
 
439
template <typename NodeType>
440
bool DependenceGraphInfo<NodeType>::getDependencies(
441
    const NodeType &Src, const NodeType &Dst, DependenceList &Deps) const {
442
  assert(Deps.empty() && "Expected empty output list at the start.");
443
 
444
  // List of memory access instructions from src and dst nodes.
445
  SmallVector<Instruction *, 8> SrcIList, DstIList;
446
  auto isMemoryAccess = [](const Instruction *I) {
447
    return I->mayReadOrWriteMemory();
448
  };
449
  Src.collectInstructions(isMemoryAccess, SrcIList);
450
  Dst.collectInstructions(isMemoryAccess, DstIList);
451
 
452
  for (auto *SrcI : SrcIList)
453
    for (auto *DstI : DstIList)
454
      if (auto Dep =
455
              const_cast<DependenceInfo *>(&DI)->depends(SrcI, DstI, true))
456
        Deps.push_back(std::move(Dep));
457
 
458
  return !Deps.empty();
459
}
460
 
461
template <typename NodeType>
462
std::string
463
DependenceGraphInfo<NodeType>::getDependenceString(const NodeType &Src,
464
                                                   const NodeType &Dst) const {
465
  std::string Str;
466
  raw_string_ostream OS(Str);
467
  DependenceList Deps;
468
  if (!getDependencies(Src, Dst, Deps))
469
    return OS.str();
470
  interleaveComma(Deps, OS, [&](const std::unique_ptr<Dependence> &D) {
471
    D->dump(OS);
472
    // Remove the extra new-line character printed by the dump
473
    // method
474
    if (OS.str().back() == '\n')
475
      OS.str().pop_back();
476
  });
477
 
478
  return OS.str();
479
}
480
 
481
//===--------------------------------------------------------------------===//
482
// GraphTraits specializations for the DDG
483
//===--------------------------------------------------------------------===//
484
 
485
/// non-const versions of the grapth trait specializations for DDG
486
template <> struct GraphTraits<DDGNode *> {
487
  using NodeRef = DDGNode *;
488
 
489
  static DDGNode *DDGGetTargetNode(DGEdge<DDGNode, DDGEdge> *P) {
490
    return &P->getTargetNode();
491
  }
492
 
493
  // Provide a mapped iterator so that the GraphTrait-based implementations can
494
  // find the target nodes without having to explicitly go through the edges.
495
  using ChildIteratorType =
496
      mapped_iterator<DDGNode::iterator, decltype(&DDGGetTargetNode)>;
497
  using ChildEdgeIteratorType = DDGNode::iterator;
498
 
499
  static NodeRef getEntryNode(NodeRef N) { return N; }
500
  static ChildIteratorType child_begin(NodeRef N) {
501
    return ChildIteratorType(N->begin(), &DDGGetTargetNode);
502
  }
503
  static ChildIteratorType child_end(NodeRef N) {
504
    return ChildIteratorType(N->end(), &DDGGetTargetNode);
505
  }
506
 
507
  static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
508
    return N->begin();
509
  }
510
  static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }
511
};
512
 
513
template <>
514
struct GraphTraits<DataDependenceGraph *> : public GraphTraits<DDGNode *> {
515
  using nodes_iterator = DataDependenceGraph::iterator;
516
  static NodeRef getEntryNode(DataDependenceGraph *DG) {
517
    return &DG->getRoot();
518
  }
519
  static nodes_iterator nodes_begin(DataDependenceGraph *DG) {
520
    return DG->begin();
521
  }
522
  static nodes_iterator nodes_end(DataDependenceGraph *DG) { return DG->end(); }
523
};
524
 
525
/// const versions of the grapth trait specializations for DDG
526
template <> struct GraphTraits<const DDGNode *> {
527
  using NodeRef = const DDGNode *;
528
 
529
  static const DDGNode *DDGGetTargetNode(const DGEdge<DDGNode, DDGEdge> *P) {
530
    return &P->getTargetNode();
531
  }
532
 
533
  // Provide a mapped iterator so that the GraphTrait-based implementations can
534
  // find the target nodes without having to explicitly go through the edges.
535
  using ChildIteratorType =
536
      mapped_iterator<DDGNode::const_iterator, decltype(&DDGGetTargetNode)>;
537
  using ChildEdgeIteratorType = DDGNode::const_iterator;
538
 
539
  static NodeRef getEntryNode(NodeRef N) { return N; }
540
  static ChildIteratorType child_begin(NodeRef N) {
541
    return ChildIteratorType(N->begin(), &DDGGetTargetNode);
542
  }
543
  static ChildIteratorType child_end(NodeRef N) {
544
    return ChildIteratorType(N->end(), &DDGGetTargetNode);
545
  }
546
 
547
  static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
548
    return N->begin();
549
  }
550
  static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }
551
};
552
 
553
template <>
554
struct GraphTraits<const DataDependenceGraph *>
555
    : public GraphTraits<const DDGNode *> {
556
  using nodes_iterator = DataDependenceGraph::const_iterator;
557
  static NodeRef getEntryNode(const DataDependenceGraph *DG) {
558
    return &DG->getRoot();
559
  }
560
  static nodes_iterator nodes_begin(const DataDependenceGraph *DG) {
561
    return DG->begin();
562
  }
563
  static nodes_iterator nodes_end(const DataDependenceGraph *DG) {
564
    return DG->end();
565
  }
566
};
567
 
568
} // namespace llvm
569
 
570
#endif // LLVM_ANALYSIS_DDG_H