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