xref: /llvm-project/llvm/lib/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.cpp (revision 1c4341d176492da5f276937b84a3d0c959e4cf5b)
1 //===- DependencyGraph.cpp ------------------------------------------===//
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 #include "llvm/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.h"
10 #include "llvm/ADT/ArrayRef.h"
11 #include "llvm/SandboxIR/Instruction.h"
12 #include "llvm/SandboxIR/Utils.h"
13 #include "llvm/Transforms/Vectorize/SandboxVectorizer/Scheduler.h"
14 
15 namespace llvm::sandboxir {
16 
17 PredIterator::value_type PredIterator::operator*() {
18   // If it's a DGNode then we dereference the operand iterator.
19   if (!isa<MemDGNode>(N)) {
20     assert(OpIt != OpItE && "Can't dereference end iterator!");
21     return DAG->getNode(cast<Instruction>((Value *)*OpIt));
22   }
23   // It's a MemDGNode, so we check if we return either the use-def operand,
24   // or a mem predecessor.
25   if (OpIt != OpItE)
26     return DAG->getNode(cast<Instruction>((Value *)*OpIt));
27   // It's a MemDGNode with OpIt == end, so we need to use MemIt.
28   assert(MemIt != cast<MemDGNode>(N)->MemPreds.end() &&
29          "Cant' dereference end iterator!");
30   return *MemIt;
31 }
32 
33 PredIterator &PredIterator::operator++() {
34   // If it's a DGNode then we increment the use-def iterator.
35   if (!isa<MemDGNode>(N)) {
36     assert(OpIt != OpItE && "Already at end!");
37     ++OpIt;
38     // Skip operands that are not instructions.
39     OpIt = skipNonInstr(OpIt, OpItE);
40     return *this;
41   }
42   // It's a MemDGNode, so if we are not at the end of the use-def iterator we
43   // need to first increment that.
44   if (OpIt != OpItE) {
45     ++OpIt;
46     // Skip operands that are not instructions.
47     OpIt = skipNonInstr(OpIt, OpItE);
48     return *this;
49   }
50   // It's a MemDGNode with OpIt == end, so we need to increment MemIt.
51   assert(MemIt != cast<MemDGNode>(N)->MemPreds.end() && "Already at end!");
52   ++MemIt;
53   return *this;
54 }
55 
56 bool PredIterator::operator==(const PredIterator &Other) const {
57   assert(DAG == Other.DAG && "Iterators of different DAGs!");
58   assert(N == Other.N && "Iterators of different nodes!");
59   return OpIt == Other.OpIt && MemIt == Other.MemIt;
60 }
61 
62 DGNode::~DGNode() {
63   if (SB == nullptr)
64     return;
65   SB->eraseFromBundle(this);
66 }
67 
68 #ifndef NDEBUG
69 void DGNode::print(raw_ostream &OS, bool PrintDeps) const {
70   OS << *I << " USuccs:" << UnscheduledSuccs << " Sched:" << Scheduled << "\n";
71 }
72 void DGNode::dump() const { print(dbgs()); }
73 void MemDGNode::print(raw_ostream &OS, bool PrintDeps) const {
74   DGNode::print(OS, false);
75   if (PrintDeps) {
76     // Print memory preds.
77     static constexpr const unsigned Indent = 4;
78     for (auto *Pred : MemPreds)
79       OS.indent(Indent) << "<-" << *Pred->getInstruction() << "\n";
80   }
81 }
82 #endif // NDEBUG
83 
84 MemDGNode *
85 MemDGNodeIntervalBuilder::getTopMemDGNode(const Interval<Instruction> &Intvl,
86                                           const DependencyGraph &DAG) {
87   Instruction *I = Intvl.top();
88   Instruction *BeforeI = Intvl.bottom();
89   // Walk down the chain looking for a mem-dep candidate instruction.
90   while (!DGNode::isMemDepNodeCandidate(I) && I != BeforeI)
91     I = I->getNextNode();
92   if (!DGNode::isMemDepNodeCandidate(I))
93     return nullptr;
94   return cast<MemDGNode>(DAG.getNode(I));
95 }
96 
97 MemDGNode *
98 MemDGNodeIntervalBuilder::getBotMemDGNode(const Interval<Instruction> &Intvl,
99                                           const DependencyGraph &DAG) {
100   Instruction *I = Intvl.bottom();
101   Instruction *AfterI = Intvl.top();
102   // Walk up the chain looking for a mem-dep candidate instruction.
103   while (!DGNode::isMemDepNodeCandidate(I) && I != AfterI)
104     I = I->getPrevNode();
105   if (!DGNode::isMemDepNodeCandidate(I))
106     return nullptr;
107   return cast<MemDGNode>(DAG.getNode(I));
108 }
109 
110 Interval<MemDGNode>
111 MemDGNodeIntervalBuilder::make(const Interval<Instruction> &Instrs,
112                                DependencyGraph &DAG) {
113   auto *TopMemN = getTopMemDGNode(Instrs, DAG);
114   // If we couldn't find a mem node in range TopN - BotN then it's empty.
115   if (TopMemN == nullptr)
116     return {};
117   auto *BotMemN = getBotMemDGNode(Instrs, DAG);
118   assert(BotMemN != nullptr && "TopMemN should be null too!");
119   // Now that we have the mem-dep nodes, create and return the range.
120   return Interval<MemDGNode>(TopMemN, BotMemN);
121 }
122 
123 DependencyGraph::DependencyType
124 DependencyGraph::getRoughDepType(Instruction *FromI, Instruction *ToI) {
125   // TODO: Perhaps compile-time improvement by skipping if neither is mem?
126   if (FromI->mayWriteToMemory()) {
127     if (ToI->mayReadFromMemory())
128       return DependencyType::ReadAfterWrite;
129     if (ToI->mayWriteToMemory())
130       return DependencyType::WriteAfterWrite;
131   } else if (FromI->mayReadFromMemory()) {
132     if (ToI->mayWriteToMemory())
133       return DependencyType::WriteAfterRead;
134   }
135   if (isa<sandboxir::PHINode>(FromI) || isa<sandboxir::PHINode>(ToI))
136     return DependencyType::Control;
137   if (ToI->isTerminator())
138     return DependencyType::Control;
139   if (DGNode::isStackSaveOrRestoreIntrinsic(FromI) ||
140       DGNode::isStackSaveOrRestoreIntrinsic(ToI))
141     return DependencyType::Other;
142   return DependencyType::None;
143 }
144 
145 static bool isOrdered(Instruction *I) {
146   auto IsOrdered = [](Instruction *I) {
147     if (auto *LI = dyn_cast<LoadInst>(I))
148       return !LI->isUnordered();
149     if (auto *SI = dyn_cast<StoreInst>(I))
150       return !SI->isUnordered();
151     if (DGNode::isFenceLike(I))
152       return true;
153     return false;
154   };
155   bool Is = IsOrdered(I);
156   assert((!Is || DGNode::isMemDepCandidate(I)) &&
157          "An ordered instruction must be a MemDepCandidate!");
158   return Is;
159 }
160 
161 bool DependencyGraph::alias(Instruction *SrcI, Instruction *DstI,
162                             DependencyType DepType) {
163   std::optional<MemoryLocation> DstLocOpt =
164       Utils::memoryLocationGetOrNone(DstI);
165   if (!DstLocOpt)
166     return true;
167   // Check aliasing.
168   assert((SrcI->mayReadFromMemory() || SrcI->mayWriteToMemory()) &&
169          "Expected a mem instr");
170   // TODO: Check AABudget
171   ModRefInfo SrcModRef =
172       isOrdered(SrcI)
173           ? ModRefInfo::ModRef
174           : Utils::aliasAnalysisGetModRefInfo(*BatchAA, SrcI, *DstLocOpt);
175   switch (DepType) {
176   case DependencyType::ReadAfterWrite:
177   case DependencyType::WriteAfterWrite:
178     return isModSet(SrcModRef);
179   case DependencyType::WriteAfterRead:
180     return isRefSet(SrcModRef);
181   default:
182     llvm_unreachable("Expected only RAW, WAW and WAR!");
183   }
184 }
185 
186 bool DependencyGraph::hasDep(Instruction *SrcI, Instruction *DstI) {
187   DependencyType RoughDepType = getRoughDepType(SrcI, DstI);
188   switch (RoughDepType) {
189   case DependencyType::ReadAfterWrite:
190   case DependencyType::WriteAfterWrite:
191   case DependencyType::WriteAfterRead:
192     return alias(SrcI, DstI, RoughDepType);
193   case DependencyType::Control:
194     // Adding actual dep edges from PHIs/to terminator would just create too
195     // many edges, which would be bad for compile-time.
196     // So we ignore them in the DAG formation but handle them in the
197     // scheduler, while sorting the ready list.
198     return false;
199   case DependencyType::Other:
200     return true;
201   case DependencyType::None:
202     return false;
203   }
204   llvm_unreachable("Unknown DependencyType enum");
205 }
206 
207 void DependencyGraph::scanAndAddDeps(MemDGNode &DstN,
208                                      const Interval<MemDGNode> &SrcScanRange) {
209   assert(isa<MemDGNode>(DstN) &&
210          "DstN is the mem dep destination, so it must be mem");
211   Instruction *DstI = DstN.getInstruction();
212   // Walk up the instruction chain from ScanRange bottom to top, looking for
213   // memory instrs that may alias.
214   for (MemDGNode &SrcN : reverse(SrcScanRange)) {
215     Instruction *SrcI = SrcN.getInstruction();
216     if (hasDep(SrcI, DstI))
217       DstN.addMemPred(&SrcN);
218   }
219 }
220 
221 void DependencyGraph::setDefUseUnscheduledSuccs(
222     const Interval<Instruction> &NewInterval) {
223   // +---+
224   // |   |  Def
225   // |   |   |
226   // |   |   v
227   // |   |  Use
228   // +---+
229   // Set the intra-interval counters in NewInterval.
230   for (Instruction &I : NewInterval) {
231     for (Value *Op : I.operands()) {
232       auto *OpI = dyn_cast<Instruction>(Op);
233       if (OpI == nullptr)
234         continue;
235       // TODO: For now don't cross BBs.
236       if (OpI->getParent() != I.getParent())
237         continue;
238       if (!NewInterval.contains(OpI))
239         continue;
240       auto *OpN = getNode(OpI);
241       if (OpN == nullptr)
242         continue;
243       ++OpN->UnscheduledSuccs;
244     }
245   }
246 
247   // Now handle the cross-interval edges.
248   bool NewIsAbove = DAGInterval.empty() || NewInterval.comesBefore(DAGInterval);
249   const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
250   const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
251   // +---+
252   // |Top|
253   // |   |  Def
254   // +---+   |
255   // |   |   v
256   // |Bot|  Use
257   // |   |
258   // +---+
259   // Walk over all instructions in "BotInterval" and update the counter
260   // of operands that are in "TopInterval".
261   for (Instruction &BotI : BotInterval) {
262     auto *BotN = getNode(&BotI);
263     // Skip scheduled nodes.
264     if (BotN->scheduled())
265       continue;
266     for (Value *Op : BotI.operands()) {
267       auto *OpI = dyn_cast<Instruction>(Op);
268       if (OpI == nullptr)
269         continue;
270       auto *OpN = getNode(OpI);
271       if (OpN == nullptr)
272         continue;
273       if (!TopInterval.contains(OpI))
274         continue;
275       ++OpN->UnscheduledSuccs;
276     }
277   }
278 }
279 
280 void DependencyGraph::createNewNodes(const Interval<Instruction> &NewInterval) {
281   // Create Nodes only for the new sections of the DAG.
282   DGNode *LastN = getOrCreateNode(NewInterval.top());
283   MemDGNode *LastMemN = dyn_cast<MemDGNode>(LastN);
284   for (Instruction &I : drop_begin(NewInterval)) {
285     auto *N = getOrCreateNode(&I);
286     // Build the Mem node chain.
287     if (auto *MemN = dyn_cast<MemDGNode>(N)) {
288       MemN->setPrevNode(LastMemN);
289       LastMemN = MemN;
290     }
291   }
292   // Link new MemDGNode chain with the old one, if any.
293   if (!DAGInterval.empty()) {
294     bool NewIsAbove = NewInterval.comesBefore(DAGInterval);
295     const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
296     const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
297     MemDGNode *LinkTopN =
298         MemDGNodeIntervalBuilder::getBotMemDGNode(TopInterval, *this);
299     MemDGNode *LinkBotN =
300         MemDGNodeIntervalBuilder::getTopMemDGNode(BotInterval, *this);
301     assert((LinkTopN == nullptr || LinkBotN == nullptr ||
302             LinkTopN->comesBefore(LinkBotN)) &&
303            "Wrong order!");
304     if (LinkTopN != nullptr && LinkBotN != nullptr) {
305       LinkTopN->setNextNode(LinkBotN);
306     }
307 #ifndef NDEBUG
308     // TODO: Remove this once we've done enough testing.
309     // Check that the chain is well formed.
310     auto UnionIntvl = DAGInterval.getUnionInterval(NewInterval);
311     MemDGNode *ChainTopN =
312         MemDGNodeIntervalBuilder::getTopMemDGNode(UnionIntvl, *this);
313     MemDGNode *ChainBotN =
314         MemDGNodeIntervalBuilder::getBotMemDGNode(UnionIntvl, *this);
315     if (ChainTopN != nullptr && ChainBotN != nullptr) {
316       for (auto *N = ChainTopN->getNextNode(), *LastN = ChainTopN; N != nullptr;
317            LastN = N, N = N->getNextNode()) {
318         assert(N == LastN->getNextNode() && "Bad chain!");
319         assert(N->getPrevNode() == LastN && "Bad chain!");
320       }
321     }
322 #endif // NDEBUG
323   }
324 
325   setDefUseUnscheduledSuccs(NewInterval);
326 }
327 
328 MemDGNode *DependencyGraph::getMemDGNodeBefore(DGNode *N, bool IncludingN,
329                                                MemDGNode *SkipN) const {
330   auto *I = N->getInstruction();
331   for (auto *PrevI = IncludingN ? I : I->getPrevNode(); PrevI != nullptr;
332        PrevI = PrevI->getPrevNode()) {
333     auto *PrevN = getNodeOrNull(PrevI);
334     if (PrevN == nullptr)
335       return nullptr;
336     auto *PrevMemN = dyn_cast<MemDGNode>(PrevN);
337     if (PrevMemN != nullptr && PrevMemN != SkipN)
338       return PrevMemN;
339   }
340   return nullptr;
341 }
342 
343 MemDGNode *DependencyGraph::getMemDGNodeAfter(DGNode *N, bool IncludingN,
344                                               MemDGNode *SkipN) const {
345   auto *I = N->getInstruction();
346   for (auto *NextI = IncludingN ? I : I->getNextNode(); NextI != nullptr;
347        NextI = NextI->getNextNode()) {
348     auto *NextN = getNodeOrNull(NextI);
349     if (NextN == nullptr)
350       return nullptr;
351     auto *NextMemN = dyn_cast<MemDGNode>(NextN);
352     if (NextMemN != nullptr && NextMemN != SkipN)
353       return NextMemN;
354   }
355   return nullptr;
356 }
357 
358 void DependencyGraph::notifyCreateInstr(Instruction *I) {
359   auto *MemN = dyn_cast<MemDGNode>(getOrCreateNode(I));
360   // TODO: Update the dependencies for the new node.
361 
362   // Update the MemDGNode chain if this is a memory node.
363   if (MemN != nullptr) {
364     if (auto *PrevMemN = getMemDGNodeBefore(MemN, /*IncludingN=*/false)) {
365       PrevMemN->NextMemN = MemN;
366       MemN->PrevMemN = PrevMemN;
367     }
368     if (auto *NextMemN = getMemDGNodeAfter(MemN, /*IncludingN=*/false)) {
369       NextMemN->PrevMemN = MemN;
370       MemN->NextMemN = NextMemN;
371     }
372   }
373 }
374 
375 void DependencyGraph::notifyMoveInstr(Instruction *I, const BBIterator &To) {
376   // NOTE: This function runs before `I` moves to its new destination.
377   BasicBlock *BB = To.getNodeParent();
378   assert(!(To != BB->end() && &*To == I->getNextNode()) &&
379          !(To == BB->end() && std::next(I->getIterator()) == BB->end()) &&
380          "Should not have been called if destination is same as origin.");
381 
382   // TODO: We can only handle fully internal movements within DAGInterval or at
383   // the borders, i.e., right before the top or right after the bottom.
384   assert(To.getNodeParent() == I->getParent() &&
385          "TODO: We don't support movement across BBs!");
386   assert(
387       (To == std::next(DAGInterval.bottom()->getIterator()) ||
388        (To != BB->end() && std::next(To) == DAGInterval.top()->getIterator()) ||
389        (To != BB->end() && DAGInterval.contains(&*To))) &&
390       "TODO: To should be either within the DAGInterval or right "
391       "before/after it.");
392 
393   // Make a copy of the DAGInterval before we update it.
394   auto OrigDAGInterval = DAGInterval;
395 
396   // Maintain the DAGInterval.
397   DAGInterval.notifyMoveInstr(I, To);
398 
399   // TODO: Perhaps check if this is legal by checking the dependencies?
400 
401   // Update the MemDGNode chain to reflect the instr movement if necessary.
402   DGNode *N = getNodeOrNull(I);
403   if (N == nullptr)
404     return;
405   MemDGNode *MemN = dyn_cast<MemDGNode>(N);
406   if (MemN == nullptr)
407     return;
408 
409   // First safely detach it from the existing chain.
410   MemN->detachFromChain();
411 
412   // Now insert it back into the chain at the new location.
413   //
414   // We won't always have a DGNode to insert before it. If `To` is BB->end() or
415   // if it points to an instr after DAGInterval.bottom() then we will have to
416   // find a node to insert *after*.
417   //
418   // BB:                              BB:
419   //  I1                               I1 ^
420   //  I2                               I2 | DAGInteval [I1 to I3]
421   //  I3                               I3 V
422   //  I4                               I4   <- `To` == right after DAGInterval
423   //    <- `To` == BB->end()
424   //
425   if (To == BB->end() ||
426       To == std::next(OrigDAGInterval.bottom()->getIterator())) {
427     // If we don't have a node to insert before, find a node to insert after and
428     // update the chain.
429     DGNode *InsertAfterN = getNode(&*std::prev(To));
430     MemN->setPrevNode(
431         getMemDGNodeBefore(InsertAfterN, /*IncludingN=*/true, /*SkipN=*/MemN));
432   } else {
433     // We have a node to insert before, so update the chain.
434     DGNode *BeforeToN = getNode(&*To);
435     MemN->setPrevNode(
436         getMemDGNodeBefore(BeforeToN, /*IncludingN=*/false, /*SkipN=*/MemN));
437     MemN->setNextNode(
438         getMemDGNodeAfter(BeforeToN, /*IncludingN=*/true, /*SkipN=*/MemN));
439   }
440 }
441 
442 void DependencyGraph::notifyEraseInstr(Instruction *I) {
443   // Update the MemDGNode chain if this is a memory node.
444   if (auto *MemN = dyn_cast_or_null<MemDGNode>(getNodeOrNull(I))) {
445     auto *PrevMemN = getMemDGNodeBefore(MemN, /*IncludingN=*/false);
446     auto *NextMemN = getMemDGNodeAfter(MemN, /*IncludingN=*/false);
447     if (PrevMemN != nullptr)
448       PrevMemN->NextMemN = NextMemN;
449     if (NextMemN != nullptr)
450       NextMemN->PrevMemN = PrevMemN;
451   }
452 
453   InstrToNodeMap.erase(I);
454 
455   // TODO: Update the dependencies.
456 }
457 
458 Interval<Instruction> DependencyGraph::extend(ArrayRef<Instruction *> Instrs) {
459   if (Instrs.empty())
460     return {};
461 
462   Interval<Instruction> InstrsInterval(Instrs);
463   Interval<Instruction> Union = DAGInterval.getUnionInterval(InstrsInterval);
464   auto NewInterval = Union.getSingleDiff(DAGInterval);
465   if (NewInterval.empty())
466     return {};
467 
468   createNewNodes(NewInterval);
469 
470   // Create the dependencies.
471   //
472   // 1. This is a new DAG, DAGInterval is empty. Fully scan the whole interval.
473   // +---+       -             -
474   // |   | SrcN  |             |
475   // |   |  |    | SrcRange    |
476   // |New|  v    |             | DstRange
477   // |   | DstN  -             |
478   // |   |                     |
479   // +---+                     -
480   // We are scanning for deps with destination in NewInterval and sources in
481   // NewInterval until DstN, for each DstN.
482   auto FullScan = [this](const Interval<Instruction> Intvl) {
483     auto DstRange = MemDGNodeIntervalBuilder::make(Intvl, *this);
484     if (!DstRange.empty()) {
485       for (MemDGNode &DstN : drop_begin(DstRange)) {
486         auto SrcRange = Interval<MemDGNode>(DstRange.top(), DstN.getPrevNode());
487         scanAndAddDeps(DstN, SrcRange);
488       }
489     }
490   };
491   if (DAGInterval.empty()) {
492     assert(NewInterval == InstrsInterval && "Expected empty DAGInterval!");
493     FullScan(NewInterval);
494   }
495   // 2. The new section is below the old section.
496   // +---+       -
497   // |   |       |
498   // |Old| SrcN  |
499   // |   |  |    |
500   // +---+  |    | SrcRange
501   // +---+  |    |             -
502   // |   |  |    |             |
503   // |New|  v    |             | DstRange
504   // |   | DstN  -             |
505   // |   |                     |
506   // +---+                     -
507   // We are scanning for deps with destination in NewInterval because the deps
508   // in DAGInterval have already been computed. We consider sources in the whole
509   // range including both NewInterval and DAGInterval until DstN, for each DstN.
510   else if (DAGInterval.bottom()->comesBefore(NewInterval.top())) {
511     auto DstRange = MemDGNodeIntervalBuilder::make(NewInterval, *this);
512     auto SrcRangeFull = MemDGNodeIntervalBuilder::make(
513         DAGInterval.getUnionInterval(NewInterval), *this);
514     for (MemDGNode &DstN : DstRange) {
515       auto SrcRange =
516           Interval<MemDGNode>(SrcRangeFull.top(), DstN.getPrevNode());
517       scanAndAddDeps(DstN, SrcRange);
518     }
519   }
520   // 3. The new section is above the old section.
521   else if (NewInterval.bottom()->comesBefore(DAGInterval.top())) {
522     // +---+       -             -
523     // |   | SrcN  |             |
524     // |New|  |    | SrcRange    | DstRange
525     // |   |  v    |             |
526     // |   | DstN  -             |
527     // |   |                     |
528     // +---+                     -
529     // +---+
530     // |Old|
531     // |   |
532     // +---+
533     // When scanning for deps with destination in NewInterval we need to fully
534     // scan the interval. This is the same as the scanning for a new DAG.
535     FullScan(NewInterval);
536 
537     // +---+       -
538     // |   |       |
539     // |New| SrcN  | SrcRange
540     // |   |  |    |
541     // |   |  |    |
542     // |   |  |    |
543     // +---+  |    -
544     // +---+  |                  -
545     // |Old|  v                  | DstRange
546     // |   | DstN                |
547     // +---+                     -
548     // When scanning for deps with destination in DAGInterval we need to
549     // consider sources from the NewInterval only, because all intra-DAGInterval
550     // dependencies have already been created.
551     auto DstRangeOld = MemDGNodeIntervalBuilder::make(DAGInterval, *this);
552     auto SrcRange = MemDGNodeIntervalBuilder::make(NewInterval, *this);
553     for (MemDGNode &DstN : DstRangeOld)
554       scanAndAddDeps(DstN, SrcRange);
555   } else {
556     llvm_unreachable("We don't expect extending in both directions!");
557   }
558 
559   DAGInterval = Union;
560   return NewInterval;
561 }
562 
563 #ifndef NDEBUG
564 void DependencyGraph::print(raw_ostream &OS) const {
565   // InstrToNodeMap is unordered so we need to create an ordered vector.
566   SmallVector<DGNode *> Nodes;
567   Nodes.reserve(InstrToNodeMap.size());
568   for (const auto &Pair : InstrToNodeMap)
569     Nodes.push_back(Pair.second.get());
570   // Sort them based on which one comes first in the BB.
571   sort(Nodes, [](DGNode *N1, DGNode *N2) {
572     return N1->getInstruction()->comesBefore(N2->getInstruction());
573   });
574   for (auto *N : Nodes)
575     N->print(OS, /*PrintDeps=*/true);
576 }
577 
578 void DependencyGraph::dump() const {
579   print(dbgs());
580   dbgs() << "\n";
581 }
582 #endif // NDEBUG
583 
584 } // namespace llvm::sandboxir
585