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