LLVM 24.0.0git
DependencyGraph.cpp
Go to the documentation of this file.
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
10#include "llvm/ADT/ArrayRef.h"
14
15namespace llvm::sandboxir {
16
17#ifndef NDEBUG
19 switch (Dir) {
21 return "BottomUp";
23 return "TopDown";
24 }
25 llvm_unreachable("Unhandled Dir!");
26}
27#endif // NDEBUG
28
29User::op_iterator PredIterator::skipBadIt(User::op_iterator OpIt,
31 const DependencyGraph &DAG) {
32 auto Skip = [&DAG](auto OpIt) {
33 auto *I = dyn_cast<Instruction>((*OpIt).get());
34 return I == nullptr || DAG.getNode(I) == nullptr;
35 };
36 while (OpIt != OpItE && Skip(OpIt))
37 ++OpIt;
38 return OpIt;
39}
40
42 // If it's a DGNode then we dereference the operand iterator.
43 if (!isa<MemDGNode>(N)) {
44 assert(OpIt != OpItE && "Can't dereference end iterator!");
45 return DAG->getNode(cast<Instruction>((Value *)*OpIt));
46 }
47 // It's a MemDGNode, so we check if we return either the use-def operand,
48 // or a mem predecessor.
49 if (OpIt != OpItE)
50 return DAG->getNode(cast<Instruction>((Value *)*OpIt));
51 // It's a MemDGNode with OpIt == end, so we need to use MemIt.
52 assert(MemIt != cast<MemDGNode>(N)->MemPreds.end() &&
53 "Cant' dereference end iterator!");
54 return *MemIt;
55}
56
57PredIterator &PredIterator::operator++() {
58 // If it's a DGNode then we increment the use-def iterator.
59 if (!isa<MemDGNode>(N)) {
60 assert(OpIt != OpItE && "Already at end!");
61 ++OpIt;
62 // Skip operands that are not instructions or are outside the DAG.
63 OpIt = PredIterator::skipBadIt(OpIt, OpItE, *DAG);
64 return *this;
65 }
66 // It's a MemDGNode, so if we are not at the end of the use-def iterator we
67 // need to first increment that.
68 if (OpIt != OpItE) {
69 ++OpIt;
70 // Skip operands that are not instructions or are outside the DAG.
71 OpIt = PredIterator::skipBadIt(OpIt, OpItE, *DAG);
72 return *this;
73 }
74 // It's a MemDGNode with OpIt == end, so we need to increment MemIt.
75 assert(MemIt != cast<MemDGNode>(N)->MemPreds.end() && "Already at end!");
76 ++MemIt;
77 return *this;
78}
79
80bool PredIterator::operator==(const PredIterator &Other) const {
81 assert(DAG == Other.DAG && "Iterators of different DAGs!");
82 assert(N == Other.N && "Iterators of different nodes!");
83 return OpIt == Other.OpIt && MemIt == Other.MemIt;
84}
85
86User::user_iterator SuccIterator::skipOutOfScope(User::user_iterator UserIt,
87 User::user_iterator UserItE,
88 const DependencyGraph &DAG) {
89 auto Skip = [&DAG](User::user_iterator UserIt) {
90 auto *I = dyn_cast<Instruction>(*UserIt);
91 return I == nullptr || DAG.getNode(I) == nullptr;
92 };
93 while (UserIt != UserItE && Skip(UserIt))
94 ++UserIt;
95 return UserIt;
96}
97
99 // If it's a DGNode then we dereference the user iterator.
100 if (!isa<MemDGNode>(N)) {
101 assert(UserIt != UserItE && "Can't dereference end iterator!");
102 return DAG->getNode(cast<Instruction>((Value *)*UserIt));
103 }
104 // It's a MemDGNode, so we check if we return either the def-use operand,
105 // or a mem predecessor.
106 if (UserIt != UserItE)
107 return DAG->getNode(cast<Instruction>((Value *)*UserIt));
108 // It's a MemDGNode with UserIt == end, so we need to use MemIt.
109 assert(MemIt != cast<MemDGNode>(N)->MemSuccs.end() &&
110 "Cant' dereference end iterator!");
111 return *MemIt;
112}
113
115 // If it's a DGNode then we increment the use-def iterator.
116 if (!isa<MemDGNode>(N)) {
117 assert(UserIt != UserItE && "Already at end!");
118 ++UserIt;
119 // Skip users that are not instructions or are outside the DAG.
120 UserIt = SuccIterator::skipOutOfScope(UserIt, UserItE, *DAG);
121 return *this;
122 }
123 // It's a MemDGNode, so if we are not at the end of the def-use iterator we
124 // need to first increment that.
125 if (UserIt != UserItE) {
126 ++UserIt;
127 // Skip operands that are not instructions or are outside the DAG.
128 UserIt = SuccIterator::skipOutOfScope(UserIt, UserItE, *DAG);
129 return *this;
130 }
131 // It's a MemDGNode with UserIt == end, so we need to increment MemIt.
132 assert(MemIt != cast<MemDGNode>(N)->MemSuccs.end() && "Already at end!");
133 ++MemIt;
134 return *this;
135}
136
137bool SuccIterator::operator==(const SuccIterator &Other) const {
138 assert(DAG == Other.DAG && "Iterators of different DAGs!");
139 assert(N == Other.N && "Iterators of different nodes!");
140 return UserIt == Other.UserIt && MemIt == Other.MemIt;
141}
142
144 if (this->SB != nullptr)
145 this->SB->eraseFromBundle(this);
146 this->SB = &SB;
147}
148
150 if (SB == nullptr)
151 return;
152 SB->eraseFromBundle(this);
153}
154
155#ifndef NDEBUG
156void DGNode::print(raw_ostream &OS, bool PrintDeps) const {
157 OS << *I << " USuccs:" << UnscheduledSuccs << " UPreds:" << UnscheduledPreds
158 << " Sched:" << Scheduled << "\n";
159}
160void DGNode::dump() const { print(dbgs()); }
161void MemDGNode::print(raw_ostream &OS, bool PrintDeps) const {
162 DGNode::print(OS, false);
163 if (PrintDeps) {
164 // Print memory preds.
165 static constexpr unsigned Indent = 4;
166 for (auto *Pred : MemPreds)
167 OS.indent(Indent) << "<-" << *Pred->getInstruction() << "\n";
168 }
169}
170#endif // NDEBUG
171
172MemDGNode *
174 const DependencyGraph &DAG) {
175 Instruction *I = Intvl.top();
176 Instruction *BeforeI = Intvl.bottom();
177 // Walk down the chain looking for a mem-dep candidate instruction.
178 while (!DGNode::isMemDepNodeCandidate(I) && I != BeforeI)
179 I = I->getNextNode();
181 return nullptr;
182 return cast<MemDGNode>(DAG.getNode(I));
183}
184
185MemDGNode *
187 const DependencyGraph &DAG) {
188 Instruction *I = Intvl.bottom();
189 Instruction *AfterI = Intvl.top();
190 // Walk up the chain looking for a mem-dep candidate instruction.
191 while (!DGNode::isMemDepNodeCandidate(I) && I != AfterI)
192 I = I->getPrevNode();
194 return nullptr;
195 return cast<MemDGNode>(DAG.getNode(I));
196}
197
200 DependencyGraph &DAG) {
201 if (Instrs.empty())
202 return {};
203 auto *TopMemN = getTopMemDGNode(Instrs, DAG);
204 // If we couldn't find a mem node in range TopN - BotN then it's empty.
205 if (TopMemN == nullptr)
206 return {};
207 auto *BotMemN = getBotMemDGNode(Instrs, DAG);
208 assert(BotMemN != nullptr && "TopMemN should be null too!");
209 // Now that we have the mem-dep nodes, create and return the range.
210 return Interval<MemDGNode>(TopMemN, BotMemN);
211}
212
213DependencyGraph::DependencyType
214DependencyGraph::getRoughDepType(Instruction *FromI, Instruction *ToI) {
215 // TODO: Perhaps compile-time improvement by skipping if neither is mem?
216 if (FromI->mayWriteToMemory()) {
217 if (ToI->mayReadFromMemory())
218 return DependencyType::ReadAfterWrite;
219 if (ToI->mayWriteToMemory())
220 return DependencyType::WriteAfterWrite;
221 } else if (FromI->mayReadFromMemory()) {
222 if (ToI->mayWriteToMemory())
223 return DependencyType::WriteAfterRead;
224 }
226 return DependencyType::Control;
227 if (ToI->isTerminator())
228 return DependencyType::Control;
231 return DependencyType::Other;
232 return DependencyType::None;
233}
234
235static bool isOrdered(Instruction *I) {
236 auto IsOrdered = [](Instruction *I) {
237 if (auto *LI = dyn_cast<LoadInst>(I))
238 return !LI->isUnordered();
239 if (auto *SI = dyn_cast<StoreInst>(I))
240 return !SI->isUnordered();
242 return true;
243 return false;
244 };
245 bool Is = IsOrdered(I);
247 "An ordered instruction must be a MemDepCandidate!");
248 return Is;
249}
250
251bool DependencyGraph::alias(Instruction *SrcI, Instruction *DstI,
252 DependencyType DepType) {
253 std::optional<MemoryLocation> DstLocOpt =
255 if (!DstLocOpt)
256 return true;
257 // Check aliasing.
258 assert((SrcI->mayReadFromMemory() || SrcI->mayWriteToMemory()) &&
259 "Expected a mem instr");
260 // TODO: Check AABudget
261 ModRefInfo SrcModRef =
262 isOrdered(SrcI)
264 : Utils::aliasAnalysisGetModRefInfo(*BatchAA, SrcI, *DstLocOpt);
265 switch (DepType) {
266 case DependencyType::ReadAfterWrite:
267 case DependencyType::WriteAfterWrite:
268 return isModSet(SrcModRef);
269 case DependencyType::WriteAfterRead:
270 return isRefSet(SrcModRef);
271 default:
272 llvm_unreachable("Expected only RAW, WAW and WAR!");
273 }
274}
275
276bool DependencyGraph::hasDep(Instruction *SrcI, Instruction *DstI) {
277 DependencyType RoughDepType = getRoughDepType(SrcI, DstI);
278 switch (RoughDepType) {
279 case DependencyType::ReadAfterWrite:
280 case DependencyType::WriteAfterWrite:
281 case DependencyType::WriteAfterRead:
282 return alias(SrcI, DstI, RoughDepType);
283 case DependencyType::Control:
284 // Adding actual dep edges from PHIs/to terminator would just create too
285 // many edges, which would be bad for compile-time.
286 // So we ignore them in the DAG formation but handle them in the
287 // scheduler, while sorting the ready list.
288 return false;
289 case DependencyType::Other:
290 return true;
291 case DependencyType::None:
292 return false;
293 }
294 llvm_unreachable("Unknown DependencyType enum");
295}
296
297void DependencyGraph::scanAndAddDeps(MemDGNode &DstN,
298 const Interval<MemDGNode> &SrcScanRange) {
299 assert(isa<MemDGNode>(DstN) &&
300 "DstN is the mem dep destination, so it must be mem");
301 Instruction *DstI = DstN.getInstruction();
302 // Walk up the instruction chain from ScanRange bottom to top, looking for
303 // memory instrs that may alias.
304 for (MemDGNode &SrcN : reverse(SrcScanRange)) {
305 Instruction *SrcI = SrcN.getInstruction();
306 if (hasDep(SrcI, DstI))
307 DstN.addMemPred(&SrcN);
308 }
309}
310
311void DependencyGraph::setDefUseUnscheduledSuccs(
312 const Interval<Instruction> &NewInterval) {
313 // +---+
314 // | | Def
315 // | | |
316 // | | v
317 // | | Use
318 // +---+
319 // Set the intra-interval counters in NewInterval.
320 for (Instruction &I : NewInterval) {
321 unsigned CntUnschedPreds = 0;
322 for (Value *Op : I.operands()) {
323 auto *OpI = dyn_cast<Instruction>(Op);
324 if (OpI == nullptr)
325 continue;
326 // TODO: For now don't cross BBs.
327 if (OpI->getParent() != I.getParent())
328 continue;
329 if (!NewInterval.contains(OpI))
330 continue;
331 auto *OpN = getNode(OpI);
332 if (OpN == nullptr)
333 continue;
334 OpN->incrUnscheduledSuccs();
335 if (!OpN->scheduled())
336 ++CntUnschedPreds;
337 }
338 getNode(&I)->UnscheduledPreds = CntUnschedPreds;
339 }
340
341 // Now handle the cross-interval edges.
342 bool NewIsAbove = DAGInterval.empty() || NewInterval.comesBefore(DAGInterval);
343 const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
344 const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
345 // +---+
346 // |Top|
347 // | | Def
348 // +---+ |
349 // | | v
350 // |Bot| Use
351 // | |
352 // +---+
353 // Walk over all instructions in "BotInterval" and update the counter
354 // of operands that are in "TopInterval".
355 for (Instruction &BotI : BotInterval) {
356 auto *BotN = getNode(&BotI);
357 // Skip scheduled nodes.
358 if (BotN->scheduled())
359 continue;
360 unsigned CntUnscheduledPreds = 0;
361 for (Value *Op : BotI.operands()) {
362 auto *OpI = dyn_cast<Instruction>(Op);
363 if (OpI == nullptr)
364 continue;
365 auto *OpN = getNode(OpI);
366 if (OpN == nullptr)
367 continue;
368 if (!TopInterval.contains(OpI))
369 continue;
370 if (!OpN->scheduled()) {
371 OpN->incrUnscheduledSuccs();
372 ++CntUnscheduledPreds;
373 }
374 }
375 *BotN->UnscheduledPreds += CntUnscheduledPreds;
376 }
377}
378
379void DependencyGraph::createNewNodes(const Interval<Instruction> &NewInterval) {
380 // Create Nodes only for the new sections of the DAG.
381 DGNode *LastN = getOrCreateNode(NewInterval.top());
382 MemDGNode *LastMemN = dyn_cast<MemDGNode>(LastN);
383 for (Instruction &I : drop_begin(NewInterval)) {
384 auto *N = getOrCreateNode(&I);
385 // Build the Mem node chain.
386 if (auto *MemN = dyn_cast<MemDGNode>(N)) {
387 MemN->setPrevNode(LastMemN);
388 LastMemN = MemN;
389 }
390 }
391 // Link new MemDGNode chain with the old one, if any.
392 if (!DAGInterval.empty()) {
393 bool NewIsAbove = NewInterval.comesBefore(DAGInterval);
394 const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
395 const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
396 MemDGNode *LinkTopN =
398 MemDGNode *LinkBotN =
400 assert((LinkTopN == nullptr || LinkBotN == nullptr ||
401 LinkTopN->comesBefore(LinkBotN)) &&
402 "Wrong order!");
403 if (LinkTopN != nullptr && LinkBotN != nullptr) {
404 LinkTopN->setNextNode(LinkBotN);
405 }
406#ifndef NDEBUG
407 // TODO: Remove this once we've done enough testing.
408 // Check that the chain is well formed.
409 auto UnionIntvl = DAGInterval.getUnionInterval(NewInterval);
410 MemDGNode *ChainTopN =
412 MemDGNode *ChainBotN =
414 if (ChainTopN != nullptr && ChainBotN != nullptr) {
415 for (auto *N = ChainTopN->getNextNode(), *LastN = ChainTopN; N != nullptr;
416 LastN = N, N = N->getNextNode()) {
417 assert(N == LastN->getNextNode() && "Bad chain!");
418 assert(N->getPrevNode() == LastN && "Bad chain!");
419 }
420 }
421#endif // NDEBUG
422 }
423
424 setDefUseUnscheduledSuccs(NewInterval);
425}
426
427MemDGNode *DependencyGraph::getMemDGNodeBefore(DGNode *N, bool IncludingN,
428 MemDGNode *SkipN) const {
429 auto *I = N->getInstruction();
430 for (auto *PrevI = IncludingN ? I : I->getPrevNode(); PrevI != nullptr;
431 PrevI = PrevI->getPrevNode()) {
432 auto *PrevN = getNodeOrNull(PrevI);
433 if (PrevN == nullptr)
434 return nullptr;
435 auto *PrevMemN = dyn_cast<MemDGNode>(PrevN);
436 if (PrevMemN != nullptr && PrevMemN != SkipN)
437 return PrevMemN;
438 }
439 return nullptr;
440}
441
442MemDGNode *DependencyGraph::getMemDGNodeAfter(DGNode *N, bool IncludingN,
443 MemDGNode *SkipN) const {
444 auto *I = N->getInstruction();
445 for (auto *NextI = IncludingN ? I : I->getNextNode(); NextI != nullptr;
446 NextI = NextI->getNextNode()) {
447 auto *NextN = getNodeOrNull(NextI);
448 if (NextN == nullptr)
449 return nullptr;
450 auto *NextMemN = dyn_cast<MemDGNode>(NextN);
451 if (NextMemN != nullptr && NextMemN != SkipN)
452 return NextMemN;
453 }
454 return nullptr;
455}
456
457void DependencyGraph::notifyCreateInstr(Instruction *I) {
458 if (Ctx->getTracker().getState() == Tracker::TrackerState::Reverting)
459 // We don't maintain the DAG while reverting.
460 return;
461 // Nothing to do if the node is not in the focus range of the DAG.
462 if (!(DAGInterval.contains(I) || DAGInterval.touches(I)))
463 return;
464 // Include `I` into the interval.
465 DAGInterval = DAGInterval.getUnionInterval({I, I});
466 auto *N = getOrCreateNode(I);
467 auto *MemN = dyn_cast<MemDGNode>(N);
468
469 // Update the MemDGNode chain if this is a memory node.
470 if (MemN != nullptr) {
471 if (auto *PrevMemN = getMemDGNodeBefore(MemN, /*IncludingN=*/false)) {
472 PrevMemN->NextMemN = MemN;
473 MemN->PrevMemN = PrevMemN;
474 }
475 if (auto *NextMemN = getMemDGNodeAfter(MemN, /*IncludingN=*/false)) {
476 NextMemN->PrevMemN = MemN;
477 MemN->NextMemN = NextMemN;
478 }
479
480 // Add Mem dependencies.
481 // 1. Scan for deps above `I` for deps to `I`: AboveN->MemN.
482 if (DAGInterval.top()->comesBefore(I)) {
483 Interval<Instruction> AboveIntvl(DAGInterval.top(), I->getPrevNode());
484 auto SrcInterval = MemDGNodeIntervalBuilder::make(AboveIntvl, *this);
485 scanAndAddDeps(*MemN, SrcInterval);
486 }
487 // 2. Scan for deps below `I` for deps from `I`: MemN->BelowN.
488 if (I->comesBefore(DAGInterval.bottom())) {
489 Interval<Instruction> BelowIntvl(I->getNextNode(), DAGInterval.bottom());
490 for (MemDGNode &BelowN :
491 MemDGNodeIntervalBuilder::make(BelowIntvl, *this))
492 scanAndAddDeps(BelowN, Interval<MemDGNode>(MemN, MemN));
493 }
494 }
495}
496
497void DependencyGraph::notifyMoveInstr(Instruction *I, const BBIterator &To) {
498 if (Ctx->getTracker().getState() == Tracker::TrackerState::Reverting)
499 // We don't maintain the DAG while reverting.
500 return;
501 // NOTE: This function runs before `I` moves to its new destination.
502 BasicBlock *BB = To.getNodeParent();
503 assert(!(To != BB->end() && &*To == I->getNextNode()) &&
504 !(To == BB->end() && std::next(I->getIterator()) == BB->end()) &&
505 "Should not have been called if destination is same as origin.");
506
507 // TODO: We can only handle fully internal movements within DAGInterval or at
508 // the borders, i.e., right before the top or right after the bottom.
509 assert(To.getNodeParent() == I->getParent() &&
510 "TODO: We don't support movement across BBs!");
511 assert(
512 (To == std::next(DAGInterval.bottom()->getIterator()) ||
513 (To != BB->end() && std::next(To) == DAGInterval.top()->getIterator()) ||
514 (To != BB->end() && DAGInterval.contains(&*To))) &&
515 "TODO: To should be either within the DAGInterval or right "
516 "before/after it.");
517
518 // Make a copy of the DAGInterval before we update it.
519 auto OrigDAGInterval = DAGInterval;
520
521 // Maintain the DAGInterval.
522 DAGInterval.notifyMoveInstr(I, To);
523
524 // TODO: Perhaps check if this is legal by checking the dependencies?
525
526 // Update the MemDGNode chain to reflect the instr movement if necessary.
528 if (N == nullptr)
529 return;
531 if (MemN == nullptr)
532 return;
533
534 // First safely detach it from the existing chain.
535 MemN->detachFromChain();
536
537 // Now insert it back into the chain at the new location.
538 //
539 // We won't always have a DGNode to insert before it. If `To` is BB->end() or
540 // if it points to an instr after DAGInterval.bottom() then we will have to
541 // find a node to insert *after*.
542 //
543 // BB: BB:
544 // I1 I1 ^
545 // I2 I2 | DAGInteval [I1 to I3]
546 // I3 I3 V
547 // I4 I4 <- `To` == right after DAGInterval
548 // <- `To` == BB->end()
549 //
550 if (To == BB->end() ||
551 To == std::next(OrigDAGInterval.bottom()->getIterator())) {
552 // If we don't have a node to insert before, find a node to insert after and
553 // update the chain.
554 DGNode *InsertAfterN = getNode(&*std::prev(To));
555 MemN->setPrevNode(
556 getMemDGNodeBefore(InsertAfterN, /*IncludingN=*/true, /*SkipN=*/MemN));
557 } else {
558 // We have a node to insert before, so update the chain.
559 DGNode *BeforeToN = getNode(&*To);
560 MemN->setPrevNode(
561 getMemDGNodeBefore(BeforeToN, /*IncludingN=*/false, /*SkipN=*/MemN));
562 MemN->setNextNode(
563 getMemDGNodeAfter(BeforeToN, /*IncludingN=*/true, /*SkipN=*/MemN));
564 }
565}
566
567void DependencyGraph::notifyEraseInstr(Instruction *I) {
568 if (Ctx->getTracker().getState() == Tracker::TrackerState::Reverting)
569 // We don't maintain the DAG while reverting.
570 return;
571 auto *N = getNode(I);
572 if (N == nullptr)
573 // Early return if there is no DAG node for `I`.
574 return;
575 if (auto *MemN = dyn_cast<MemDGNode>(getNode(I))) {
576 // Update the MemDGNode chain if this is a memory node.
577 auto *PrevMemN = getMemDGNodeBefore(MemN, /*IncludingN=*/false);
578 auto *NextMemN = getMemDGNodeAfter(MemN, /*IncludingN=*/false);
579 if (PrevMemN != nullptr)
580 PrevMemN->NextMemN = NextMemN;
581 if (NextMemN != nullptr)
582 NextMemN->PrevMemN = PrevMemN;
583
584 // Drop the memory dependencies from both predecessors and successors.
585 while (!MemN->memPreds().empty()) {
586 auto *PredN = *MemN->memPreds().begin();
587 MemN->removeMemPred(PredN);
588 }
589 while (!MemN->memSuccs().empty()) {
590 auto *SuccN = *MemN->memSuccs().begin();
591 SuccN->removeMemPred(MemN);
592 }
593 // NOTE: The unscheduled succs for MemNodes get updated be setMemPred().
594 } else {
595 // If this is a non-mem node we only need to update UnscheduledSuccs.
596 if (!N->scheduled()) {
597 for (auto *PredN : N->preds(*this))
598 if (!PredN->scheduled())
599 PredN->decrUnscheduledSuccs();
600 for (auto *SuccN : N->succs(*this))
601 /// TODO: Does the successor also need to be guarded?
602 SuccN->decrUnscheduledPreds();
603 }
604 }
605 // Finally erase the Node.
606 InstrToNodeMap.erase(I);
607}
608
609void DependencyGraph::notifySetUse(const Use &U, Value *NewSrc) {
610 // If U.User is not in the DAG, then we should not attempt to decrement
611 // CurrSrcN's unscheduled successors.
612 // ------- ------- -
613 // CurrSrc | DAG interval
614 // | NewSrc |
615 // ---|--- ---|--- -
616 // U.User U.User
617 auto *UserI = dyn_cast_or_null<Instruction>(U.getUser());
618 if (UserI == nullptr)
619 return;
620 auto *UserN = getNode(UserI);
621 if (UserN == nullptr)
622 return;
623 // If UserN is marked as scheduled then we should not update CrrSrcN' or
624 // NewSrcN's unscheduled successors.
625 if (UserN->scheduled())
626 return;
627 // Update the UnscheduledSuccs counter for both the current source and
628 // NewSrc if needed.
629 if (auto *CurrSrcI = dyn_cast<Instruction>(U.get())) {
630 if (auto *CurrSrcN = getNode(CurrSrcI)) {
631 // If CurrSrcN is scheduled there is no point in updating UnscheduleSuccs.
632 if (!CurrSrcN->scheduled()) {
633 if (Dir == SchedDirection::BottomUp)
634 CurrSrcN->decrUnscheduledSuccs();
635 else
636 UserN->decrUnscheduledPreds();
637 }
638 }
639 }
640 if (auto *NewSrcI = dyn_cast<Instruction>(NewSrc)) {
641 if (auto *NewSrcN = getNode(NewSrcI)) {
642 // If CurrSrcN is scheduled there is no point in updating UnscheduleSuccs.
643 if (!NewSrcN->scheduled()) {
644 if (Dir == SchedDirection::BottomUp)
645 NewSrcN->incrUnscheduledSuccs();
646 else
647 UserN->incrUnscheduledPreds();
648 }
649 }
650 }
651}
652
654 if (Instrs.empty())
655 return {};
656
657 Interval<Instruction> InstrsInterval(Instrs);
658 Interval<Instruction> Union = DAGInterval.getUnionInterval(InstrsInterval);
659 auto NewInterval = Union.getSingleDiff(DAGInterval);
660 if (NewInterval.empty())
661 return {};
662
663 createNewNodes(NewInterval);
664
665 // Create the dependencies.
666 //
667 // 1. This is a new DAG, DAGInterval is empty. Fully scan the whole interval.
668 // +---+ - -
669 // | | SrcN | |
670 // | | | | SrcRange |
671 // |New| v | | DstRange
672 // | | DstN - |
673 // | | |
674 // +---+ -
675 // We are scanning for deps with destination in NewInterval and sources in
676 // NewInterval until DstN, for each DstN.
677 auto FullScan = [this](const Interval<Instruction> Intvl) {
678 auto DstRange = MemDGNodeIntervalBuilder::make(Intvl, *this);
679 if (!DstRange.empty()) {
680 for (MemDGNode &DstN : drop_begin(DstRange)) {
681 auto SrcRange = Interval<MemDGNode>(DstRange.top(), DstN.getPrevNode());
682 scanAndAddDeps(DstN, SrcRange);
683 }
684 }
685 };
686 auto MemDAGInterval = MemDGNodeIntervalBuilder::make(DAGInterval, *this);
687 if (MemDAGInterval.empty()) {
688 FullScan(NewInterval);
689 }
690 // 2. The new section is below the old section.
691 // +---+ -
692 // | | |
693 // |Old| SrcN |
694 // | | | |
695 // +---+ | | SrcRange
696 // +---+ | | -
697 // | | | | |
698 // |New| v | | DstRange
699 // | | DstN - |
700 // | | |
701 // +---+ -
702 // We are scanning for deps with destination in NewInterval because the deps
703 // in DAGInterval have already been computed. We consider sources in the whole
704 // range including both NewInterval and DAGInterval until DstN, for each DstN.
705 else if (DAGInterval.bottom()->comesBefore(NewInterval.top())) {
706 auto DstRange = MemDGNodeIntervalBuilder::make(NewInterval, *this);
707 auto SrcRangeFull = MemDAGInterval.getUnionInterval(DstRange);
708 for (MemDGNode &DstN : DstRange) {
709 auto SrcRange =
710 Interval<MemDGNode>(SrcRangeFull.top(), DstN.getPrevNode());
711 scanAndAddDeps(DstN, SrcRange);
712 }
713 }
714 // 3. The new section is above the old section.
715 else if (NewInterval.bottom()->comesBefore(DAGInterval.top())) {
716 // +---+ - -
717 // | | SrcN | |
718 // |New| | | SrcRange | DstRange
719 // | | v | |
720 // | | DstN - |
721 // | | |
722 // +---+ -
723 // +---+
724 // |Old|
725 // | |
726 // +---+
727 // When scanning for deps with destination in NewInterval we need to fully
728 // scan the interval. This is the same as the scanning for a new DAG.
729 FullScan(NewInterval);
730
731 // +---+ -
732 // | | |
733 // |New| SrcN | SrcRange
734 // | | | |
735 // | | | |
736 // | | | |
737 // +---+ | -
738 // +---+ | -
739 // |Old| v | DstRange
740 // | | DstN |
741 // +---+ -
742 // When scanning for deps with destination in DAGInterval we need to
743 // consider sources from the NewInterval only, because all intra-DAGInterval
744 // dependencies have already been created.
745 auto DstRangeOld = MemDAGInterval;
746 auto SrcRange = MemDGNodeIntervalBuilder::make(NewInterval, *this);
747 for (MemDGNode &DstN : DstRangeOld)
748 scanAndAddDeps(DstN, SrcRange);
749 } else {
750 llvm_unreachable("We don't expect extending in both directions!");
751 }
752
753 DAGInterval = Union;
754 return NewInterval;
755}
756
757#ifndef NDEBUG
759 // InstrToNodeMap is unordered so we need to create an ordered vector.
761 Nodes.reserve(InstrToNodeMap.size());
762 for (const auto &Pair : InstrToNodeMap)
763 Nodes.push_back(Pair.second.get());
764 // Sort them based on which one comes first in the BB.
765 sort(Nodes, [](DGNode *N1, DGNode *N2) {
766 return N1->getInstruction()->comesBefore(N2->getInstruction());
767 });
768 for (auto *N : Nodes)
769 N->print(OS, /*PrintDeps=*/true);
770}
771
773 print(dbgs());
774 dbgs() << "\n";
775}
776#endif // NDEBUG
777
778} // namespace llvm::sandboxir
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define I(x, y, z)
Definition MD5.cpp:57
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
LLVM_ABI bool mayWriteToMemory() const LLVM_READONLY
Return true if this instruction may modify memory.
LLVM_ABI bool mayReadFromMemory() const LLVM_READONLY
Return true if this instruction may read memory.
void reserve(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
A wrapper around a string literal that serves as a proxy for constructing global tables of StringRefs...
Definition StringRef.h:888
Use * op_iterator
Definition User.h:254
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
raw_ostream & indent(unsigned NumSpaces)
indent - Insert 'NumSpaces' spaces.
A DependencyGraph Node that points to an Instruction and contains memory dependency edges.
virtual void print(raw_ostream &OS, bool PrintDeps=true) const
static bool isMemDepCandidate(Instruction *I)
We consider I as a Memory Dependency Candidate instruction if it reads/write memory or if it has side...
void setSchedBundle(SchedBundle &SB)
SchedBundle * SB
The scheduler bundle that this node belongs to.
bool Scheduled
This is true if this node has been scheduled.
std::optional< unsigned > UnscheduledSuccs
The number of unscheduled successors.
static bool isMemDepNodeCandidate(Instruction *I)
\Returns true if I is a memory dependency candidate instruction.
static bool isFenceLike(Instruction *I)
\Returns true if I is fence like. It excludes non-mem intrinsics.
LLVM_DUMP_METHOD void dump() const
Instruction * getInstruction() const
static bool isStackSaveOrRestoreIntrinsic(Instruction *I)
std::optional< unsigned > UnscheduledPreds
LLVM_DUMP_METHOD void dump() const
DGNode * getNode(Instruction *I) const
DGNode * getNodeOrNull(Instruction *I) const
Like getNode() but returns nullptr if I is nullptr.
void print(raw_ostream &OS) const
DGNode * getOrCreateNode(Instruction *I)
LLVM_ABI Interval< Instruction > extend(ArrayRef< Instruction * > Instrs)
Build/extend the dependency graph such that it includes Instrs.
A sandboxir::User with operands, opcode and linked with previous/next instructions in an instruction ...
Definition Instruction.h:43
bool comesBefore(const Instruction *Other) const
Given an instruction Other in the same basic block as this instruction, return true if this instructi...
static LLVM_ABI MemDGNode * getBotMemDGNode(const Interval< Instruction > &Intvl, const DependencyGraph &DAG)
Scans the instruction chain in Intvl bottom-up, returning the bottom-most MemDGNode,...
static LLVM_ABI MemDGNode * getTopMemDGNode(const Interval< Instruction > &Intvl, const DependencyGraph &DAG)
Scans the instruction chain in Intvl top-down, returning the top-most MemDGNode, or nullptr.
static LLVM_ABI Interval< MemDGNode > make(const Interval< Instruction > &Instrs, DependencyGraph &DAG)
Given Instrs it finds their closest mem nodes in the interval and returns the corresponding mem range...
A DependencyGraph Node for instructions that may read/write memory, or have some ordering constraints...
void print(raw_ostream &OS, bool PrintDeps=true) const override
LLVM_ABI value_type operator*()
LLVM_ABI PredIterator & operator++()
LLVM_ABI bool operator==(const PredIterator &Other) const
LLVM_ABI value_type operator*()
LLVM_ABI bool operator==(const SuccIterator &Other) const
LLVM_ABI SuccIterator & operator++()
Represents a Def-use/Use-def edge in SandboxIR.
Definition Use.h:43
static ModRefInfo aliasAnalysisGetModRefInfo(BatchAAResults &BatchAA, const Instruction *I, const std::optional< MemoryLocation > &OptLoc)
Equivalent to BatchAA::getModRefInfo().
Definition Utils.h:124
static std::optional< llvm::MemoryLocation > memoryLocationGetOrNone(const Instruction *I)
Equivalent to MemoryLocation::getOrNone(I).
Definition Utils.h:85
A SandboxIR Value has users. This is the base class.
Definition Value.h:72
mapped_iterator< sandboxir::UserUseIterator, UseToUser > user_iterator
Definition Value.h:239
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
static bool isOrdered(Instruction *I)
StringLiteral schedDirectionToStr(SchedDirection Dir)
template class LLVM_TEMPLATE_ABI Interval< MemDGNode >
Definition Interval.cpp:47
BasicBlock(llvm::BasicBlock *BB, Context &SBCtx)
Definition BasicBlock.h:75
template class LLVM_TEMPLATE_ABI Interval< Instruction >
Definition Interval.cpp:46
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:315
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
bool isModSet(const ModRefInfo MRI)
Definition ModRef.h:49
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
ModRefInfo
Flags indicating whether a memory access modifies or references memory.
Definition ModRef.h:28
@ ModRef
The access may reference and may modify the value stored in memory.
Definition ModRef.h:36
@ Other
Any other memory.
Definition ModRef.h:68
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
bool isRefSet(const ModRefInfo MRI)
Definition ModRef.h:52
#define N