LLVM 20.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
18 // If it's a DGNode then we dereference the operand iterator.
19 if (!isa<MemDGNode>(N)) {
20 assert(OpIt != OpItE && "Can't dereference end iterator!");
21 return DAG->getNode(cast<Instruction>((Value *)*OpIt));
22 }
23 // It's a MemDGNode, so we check if we return either the use-def operand,
24 // or a mem predecessor.
25 if (OpIt != OpItE)
26 return DAG->getNode(cast<Instruction>((Value *)*OpIt));
27 // It's a MemDGNode with OpIt == end, so we need to use MemIt.
28 assert(MemIt != cast<MemDGNode>(N)->MemPreds.end() &&
29 "Cant' dereference end iterator!");
30 return *MemIt;
31}
32
34 // If it's a DGNode then we increment the use-def iterator.
35 if (!isa<MemDGNode>(N)) {
36 assert(OpIt != OpItE && "Already at end!");
37 ++OpIt;
38 // Skip operands that are not instructions.
39 OpIt = skipNonInstr(OpIt, OpItE);
40 return *this;
41 }
42 // It's a MemDGNode, so if we are not at the end of the use-def iterator we
43 // need to first increment that.
44 if (OpIt != OpItE) {
45 ++OpIt;
46 // Skip operands that are not instructions.
47 OpIt = skipNonInstr(OpIt, OpItE);
48 return *this;
49 }
50 // It's a MemDGNode with OpIt == end, so we need to increment MemIt.
51 assert(MemIt != cast<MemDGNode>(N)->MemPreds.end() && "Already at end!");
52 ++MemIt;
53 return *this;
54}
55
57 assert(DAG == Other.DAG && "Iterators of different DAGs!");
58 assert(N == Other.N && "Iterators of different nodes!");
59 return OpIt == Other.OpIt && MemIt == Other.MemIt;
60}
61
63 if (SB == nullptr)
64 return;
65 SB->eraseFromBundle(this);
66}
67
68#ifndef NDEBUG
69void DGNode::print(raw_ostream &OS, bool PrintDeps) const {
70 OS << *I << " USuccs:" << UnscheduledSuccs << " Sched:" << Scheduled << "\n";
71}
72void DGNode::dump() const { print(dbgs()); }
73void MemDGNode::print(raw_ostream &OS, bool PrintDeps) const {
74 DGNode::print(OS, false);
75 if (PrintDeps) {
76 // Print memory preds.
77 static constexpr const unsigned Indent = 4;
78 for (auto *Pred : MemPreds)
79 OS.indent(Indent) << "<-" << *Pred->getInstruction() << "\n";
80 }
81}
82#endif // NDEBUG
83
86 const DependencyGraph &DAG) {
87 Instruction *I = Intvl.top();
88 Instruction *BeforeI = Intvl.bottom();
89 // Walk down the chain looking for a mem-dep candidate instruction.
90 while (!DGNode::isMemDepNodeCandidate(I) && I != BeforeI)
91 I = I->getNextNode();
93 return nullptr;
94 return cast<MemDGNode>(DAG.getNode(I));
95}
96
99 const DependencyGraph &DAG) {
100 Instruction *I = Intvl.bottom();
101 Instruction *AfterI = Intvl.top();
102 // Walk up the chain looking for a mem-dep candidate instruction.
103 while (!DGNode::isMemDepNodeCandidate(I) && I != AfterI)
104 I = I->getPrevNode();
106 return nullptr;
107 return cast<MemDGNode>(DAG.getNode(I));
108}
109
112 DependencyGraph &DAG) {
113 auto *TopMemN = getTopMemDGNode(Instrs, DAG);
114 // If we couldn't find a mem node in range TopN - BotN then it's empty.
115 if (TopMemN == nullptr)
116 return {};
117 auto *BotMemN = getBotMemDGNode(Instrs, DAG);
118 assert(BotMemN != nullptr && "TopMemN should be null too!");
119 // Now that we have the mem-dep nodes, create and return the range.
120 return Interval<MemDGNode>(TopMemN, BotMemN);
121}
122
123DependencyGraph::DependencyType
124DependencyGraph::getRoughDepType(Instruction *FromI, Instruction *ToI) {
125 // TODO: Perhaps compile-time improvement by skipping if neither is mem?
126 if (FromI->mayWriteToMemory()) {
127 if (ToI->mayReadFromMemory())
128 return DependencyType::ReadAfterWrite;
129 if (ToI->mayWriteToMemory())
130 return DependencyType::WriteAfterWrite;
131 } else if (FromI->mayReadFromMemory()) {
132 if (ToI->mayWriteToMemory())
133 return DependencyType::WriteAfterRead;
134 }
135 if (isa<sandboxir::PHINode>(FromI) || isa<sandboxir::PHINode>(ToI))
136 return DependencyType::Control;
137 if (ToI->isTerminator())
138 return DependencyType::Control;
141 return DependencyType::Other;
142 return DependencyType::None;
143}
144
145static bool isOrdered(Instruction *I) {
146 auto IsOrdered = [](Instruction *I) {
147 if (auto *LI = dyn_cast<LoadInst>(I))
148 return !LI->isUnordered();
149 if (auto *SI = dyn_cast<StoreInst>(I))
150 return !SI->isUnordered();
152 return true;
153 return false;
154 };
155 bool Is = IsOrdered(I);
157 "An ordered instruction must be a MemDepCandidate!");
158 return Is;
159}
160
161bool DependencyGraph::alias(Instruction *SrcI, Instruction *DstI,
162 DependencyType DepType) {
163 std::optional<MemoryLocation> DstLocOpt =
165 if (!DstLocOpt)
166 return true;
167 // Check aliasing.
168 assert((SrcI->mayReadFromMemory() || SrcI->mayWriteToMemory()) &&
169 "Expected a mem instr");
170 // TODO: Check AABudget
171 ModRefInfo SrcModRef =
172 isOrdered(SrcI)
174 : Utils::aliasAnalysisGetModRefInfo(*BatchAA, SrcI, *DstLocOpt);
175 switch (DepType) {
176 case DependencyType::ReadAfterWrite:
177 case DependencyType::WriteAfterWrite:
178 return isModSet(SrcModRef);
179 case DependencyType::WriteAfterRead:
180 return isRefSet(SrcModRef);
181 default:
182 llvm_unreachable("Expected only RAW, WAW and WAR!");
183 }
184}
185
186bool DependencyGraph::hasDep(Instruction *SrcI, Instruction *DstI) {
187 DependencyType RoughDepType = getRoughDepType(SrcI, DstI);
188 switch (RoughDepType) {
189 case DependencyType::ReadAfterWrite:
190 case DependencyType::WriteAfterWrite:
191 case DependencyType::WriteAfterRead:
192 return alias(SrcI, DstI, RoughDepType);
193 case DependencyType::Control:
194 // Adding actual dep edges from PHIs/to terminator would just create too
195 // many edges, which would be bad for compile-time.
196 // So we ignore them in the DAG formation but handle them in the
197 // scheduler, while sorting the ready list.
198 return false;
199 case DependencyType::Other:
200 return true;
201 case DependencyType::None:
202 return false;
203 }
204 llvm_unreachable("Unknown DependencyType enum");
205}
206
207void DependencyGraph::scanAndAddDeps(MemDGNode &DstN,
208 const Interval<MemDGNode> &SrcScanRange) {
209 assert(isa<MemDGNode>(DstN) &&
210 "DstN is the mem dep destination, so it must be mem");
211 Instruction *DstI = DstN.getInstruction();
212 // Walk up the instruction chain from ScanRange bottom to top, looking for
213 // memory instrs that may alias.
214 for (MemDGNode &SrcN : reverse(SrcScanRange)) {
215 Instruction *SrcI = SrcN.getInstruction();
216 if (hasDep(SrcI, DstI))
217 DstN.addMemPred(&SrcN);
218 }
219}
220
221void DependencyGraph::setDefUseUnscheduledSuccs(
222 const Interval<Instruction> &NewInterval) {
223 // +---+
224 // | | Def
225 // | | |
226 // | | v
227 // | | Use
228 // +---+
229 // Set the intra-interval counters in NewInterval.
230 for (Instruction &I : NewInterval) {
231 for (Value *Op : I.operands()) {
232 auto *OpI = dyn_cast<Instruction>(Op);
233 if (OpI == nullptr)
234 continue;
235 if (!NewInterval.contains(OpI))
236 continue;
237 auto *OpN = getNode(OpI);
238 if (OpN == nullptr)
239 continue;
240 ++OpN->UnscheduledSuccs;
241 }
242 }
243
244 // Now handle the cross-interval edges.
245 bool NewIsAbove = DAGInterval.empty() || NewInterval.comesBefore(DAGInterval);
246 const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
247 const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
248 // +---+
249 // |Top|
250 // | | Def
251 // +---+ |
252 // | | v
253 // |Bot| Use
254 // | |
255 // +---+
256 // Walk over all instructions in "BotInterval" and update the counter
257 // of operands that are in "TopInterval".
258 for (Instruction &BotI : BotInterval) {
259 auto *BotN = getNode(&BotI);
260 // Skip scheduled nodes.
261 if (BotN->scheduled())
262 continue;
263 for (Value *Op : BotI.operands()) {
264 auto *OpI = dyn_cast<Instruction>(Op);
265 if (OpI == nullptr)
266 continue;
267 if (!TopInterval.contains(OpI))
268 continue;
269 auto *OpN = getNode(OpI);
270 if (OpN == nullptr)
271 continue;
272 ++OpN->UnscheduledSuccs;
273 }
274 }
275}
276
277void DependencyGraph::createNewNodes(const Interval<Instruction> &NewInterval) {
278 // Create Nodes only for the new sections of the DAG.
279 DGNode *LastN = getOrCreateNode(NewInterval.top());
280 MemDGNode *LastMemN = dyn_cast<MemDGNode>(LastN);
281 for (Instruction &I : drop_begin(NewInterval)) {
282 auto *N = getOrCreateNode(&I);
283 // Build the Mem node chain.
284 if (auto *MemN = dyn_cast<MemDGNode>(N)) {
285 MemN->setPrevNode(LastMemN);
286 if (LastMemN != nullptr)
287 LastMemN->setNextNode(MemN);
288 LastMemN = MemN;
289 }
290 }
291 // Link new MemDGNode chain with the old one, if any.
292 if (!DAGInterval.empty()) {
293 bool NewIsAbove = NewInterval.comesBefore(DAGInterval);
294 const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
295 const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
296 MemDGNode *LinkTopN =
298 MemDGNode *LinkBotN =
300 assert((LinkTopN == nullptr || LinkBotN == nullptr ||
301 LinkTopN->comesBefore(LinkBotN)) &&
302 "Wrong order!");
303 if (LinkTopN != nullptr && LinkBotN != nullptr) {
304 LinkTopN->setNextNode(LinkBotN);
305 LinkBotN->setPrevNode(LinkTopN);
306 }
307#ifndef NDEBUG
308 // TODO: Remove this once we've done enough testing.
309 // Check that the chain is well formed.
310 auto UnionIntvl = DAGInterval.getUnionInterval(NewInterval);
311 MemDGNode *ChainTopN =
313 MemDGNode *ChainBotN =
315 if (ChainTopN != nullptr && ChainBotN != nullptr) {
316 for (auto *N = ChainTopN->getNextNode(), *LastN = ChainTopN; N != nullptr;
317 LastN = N, N = N->getNextNode()) {
318 assert(N == LastN->getNextNode() && "Bad chain!");
319 assert(N->getPrevNode() == LastN && "Bad chain!");
320 }
321 }
322#endif // NDEBUG
323 }
324
325 setDefUseUnscheduledSuccs(NewInterval);
326}
327
328MemDGNode *DependencyGraph::getMemDGNodeBefore(DGNode *N,
329 bool IncludingN) const {
330 auto *I = N->getInstruction();
331 for (auto *PrevI = IncludingN ? I : I->getPrevNode(); PrevI != nullptr;
332 PrevI = PrevI->getPrevNode()) {
333 auto *PrevN = getNodeOrNull(PrevI);
334 if (PrevN == nullptr)
335 return nullptr;
336 if (auto *PrevMemN = dyn_cast<MemDGNode>(PrevN))
337 return PrevMemN;
338 }
339 return nullptr;
340}
341
342MemDGNode *DependencyGraph::getMemDGNodeAfter(DGNode *N,
343 bool IncludingN) const {
344 auto *I = N->getInstruction();
345 for (auto *NextI = IncludingN ? I : I->getNextNode(); NextI != nullptr;
346 NextI = NextI->getNextNode()) {
347 auto *NextN = getNodeOrNull(NextI);
348 if (NextN == nullptr)
349 return nullptr;
350 if (auto *NextMemN = dyn_cast<MemDGNode>(NextN))
351 return NextMemN;
352 }
353 return nullptr;
354}
355
356void DependencyGraph::notifyCreateInstr(Instruction *I) {
357 auto *MemN = dyn_cast<MemDGNode>(getOrCreateNode(I));
358 // TODO: Update the dependencies for the new node.
359
360 // Update the MemDGNode chain if this is a memory node.
361 if (MemN != nullptr) {
362 if (auto *PrevMemN = getMemDGNodeBefore(MemN, /*IncludingN=*/false)) {
363 PrevMemN->NextMemN = MemN;
364 MemN->PrevMemN = PrevMemN;
365 }
366 if (auto *NextMemN = getMemDGNodeAfter(MemN, /*IncludingN=*/false)) {
367 NextMemN->PrevMemN = MemN;
368 MemN->NextMemN = NextMemN;
369 }
370 }
371}
372
373void DependencyGraph::notifyEraseInstr(Instruction *I) {
374 // Update the MemDGNode chain if this is a memory node.
375 if (auto *MemN = dyn_cast_or_null<MemDGNode>(getNodeOrNull(I))) {
376 auto *PrevMemN = getMemDGNodeBefore(MemN, /*IncludingN=*/false);
377 auto *NextMemN = getMemDGNodeAfter(MemN, /*IncludingN=*/false);
378 if (PrevMemN != nullptr)
379 PrevMemN->NextMemN = NextMemN;
380 if (NextMemN != nullptr)
381 NextMemN->PrevMemN = PrevMemN;
382 }
383
384 InstrToNodeMap.erase(I);
385
386 // TODO: Update the dependencies.
387}
388
390 if (Instrs.empty())
391 return {};
392
393 Interval<Instruction> InstrsInterval(Instrs);
394 Interval<Instruction> Union = DAGInterval.getUnionInterval(InstrsInterval);
395 auto NewInterval = Union.getSingleDiff(DAGInterval);
396 if (NewInterval.empty())
397 return {};
398
399 createNewNodes(NewInterval);
400
401 // Create the dependencies.
402 //
403 // 1. This is a new DAG, DAGInterval is empty. Fully scan the whole interval.
404 // +---+ - -
405 // | | SrcN | |
406 // | | | | SrcRange |
407 // |New| v | | DstRange
408 // | | DstN - |
409 // | | |
410 // +---+ -
411 // We are scanning for deps with destination in NewInterval and sources in
412 // NewInterval until DstN, for each DstN.
413 auto FullScan = [this](const Interval<Instruction> Intvl) {
414 auto DstRange = MemDGNodeIntervalBuilder::make(Intvl, *this);
415 if (!DstRange.empty()) {
416 for (MemDGNode &DstN : drop_begin(DstRange)) {
417 auto SrcRange = Interval<MemDGNode>(DstRange.top(), DstN.getPrevNode());
418 scanAndAddDeps(DstN, SrcRange);
419 }
420 }
421 };
422 if (DAGInterval.empty()) {
423 assert(NewInterval == InstrsInterval && "Expected empty DAGInterval!");
424 FullScan(NewInterval);
425 }
426 // 2. The new section is below the old section.
427 // +---+ -
428 // | | |
429 // |Old| SrcN |
430 // | | | |
431 // +---+ | | SrcRange
432 // +---+ | | -
433 // | | | | |
434 // |New| v | | DstRange
435 // | | DstN - |
436 // | | |
437 // +---+ -
438 // We are scanning for deps with destination in NewInterval because the deps
439 // in DAGInterval have already been computed. We consider sources in the whole
440 // range including both NewInterval and DAGInterval until DstN, for each DstN.
441 else if (DAGInterval.bottom()->comesBefore(NewInterval.top())) {
442 auto DstRange = MemDGNodeIntervalBuilder::make(NewInterval, *this);
443 auto SrcRangeFull = MemDGNodeIntervalBuilder::make(
444 DAGInterval.getUnionInterval(NewInterval), *this);
445 for (MemDGNode &DstN : DstRange) {
446 auto SrcRange =
447 Interval<MemDGNode>(SrcRangeFull.top(), DstN.getPrevNode());
448 scanAndAddDeps(DstN, SrcRange);
449 }
450 }
451 // 3. The new section is above the old section.
452 else if (NewInterval.bottom()->comesBefore(DAGInterval.top())) {
453 // +---+ - -
454 // | | SrcN | |
455 // |New| | | SrcRange | DstRange
456 // | | v | |
457 // | | DstN - |
458 // | | |
459 // +---+ -
460 // +---+
461 // |Old|
462 // | |
463 // +---+
464 // When scanning for deps with destination in NewInterval we need to fully
465 // scan the interval. This is the same as the scanning for a new DAG.
466 FullScan(NewInterval);
467
468 // +---+ -
469 // | | |
470 // |New| SrcN | SrcRange
471 // | | | |
472 // | | | |
473 // | | | |
474 // +---+ | -
475 // +---+ | -
476 // |Old| v | DstRange
477 // | | DstN |
478 // +---+ -
479 // When scanning for deps with destination in DAGInterval we need to
480 // consider sources from the NewInterval only, because all intra-DAGInterval
481 // dependencies have already been created.
482 auto DstRangeOld = MemDGNodeIntervalBuilder::make(DAGInterval, *this);
483 auto SrcRange = MemDGNodeIntervalBuilder::make(NewInterval, *this);
484 for (MemDGNode &DstN : DstRangeOld)
485 scanAndAddDeps(DstN, SrcRange);
486 } else {
487 llvm_unreachable("We don't expect extending in both directions!");
488 }
489
490 DAGInterval = Union;
491 return NewInterval;
492}
493
494#ifndef NDEBUG
496 // InstrToNodeMap is unordered so we need to create an ordered vector.
498 Nodes.reserve(InstrToNodeMap.size());
499 for (const auto &Pair : InstrToNodeMap)
500 Nodes.push_back(Pair.second.get());
501 // Sort them based on which one comes first in the BB.
502 sort(Nodes, [](DGNode *N1, DGNode *N2) {
503 return N1->getInstruction()->comesBefore(N2->getInstruction());
504 });
505 for (auto *N : Nodes)
506 N->print(OS, /*PrintDeps=*/true);
507}
508
510 print(dbgs());
511 dbgs() << "\n";
512}
513#endif // NDEBUG
514
515} // namespace llvm::sandboxir
#define I(x, y, z)
Definition: MD5.cpp:58
std::pair< uint64_t, uint64_t > Interval
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
bool empty() const
empty - Check if the array is empty.
Definition: ArrayRef.h:163
bool mayWriteToMemory() const LLVM_READONLY
Return true if this instruction may modify memory.
bool mayReadFromMemory() const LLVM_READONLY
Return true if this instruction may read memory.
void reserve(size_type N)
Definition: SmallVector.h:663
void push_back(const T &Elt)
Definition: SmallVector.h:413
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
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...
unsigned UnscheduledSuccs
The number of unscheduled successors.
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
DGNode * getNodeOrNull(Instruction *I) const
Like getNode() but returns nullptr if I is nullptr.
void print(raw_ostream &OS) const
DGNode * getOrCreateNode(Instruction *I)
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:42
static MemDGNode * getBotMemDGNode(const Interval< Instruction > &Intvl, const DependencyGraph &DAG)
Scans the instruction chain in Intvl bottom-up, returning the bottom-most MemDGNode,...
static 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 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...
virtual void print(raw_ostream &OS, bool PrintDeps=true) const override
Iterate over both def-use and mem dependencies.
bool operator==(const PredIterator &Other) const
static ModRefInfo aliasAnalysisGetModRefInfo(BatchAAResults &BatchAA, const Instruction *I, const std::optional< MemoryLocation > &OptLoc)
Equivalent to BatchAA::getModRefInfo().
Definition: Utils.h:116
static std::optional< llvm::MemoryLocation > memoryLocationGetOrNone(const Instruction *I)
Equivalent to MemoryLocation::getOrNone(I).
Definition: Utils.h:78
A SandboxIR Value has users. This is the base class.
Definition: Value.h:63
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
static bool isOrdered(Instruction *I)
static User::op_iterator skipNonInstr(User::op_iterator OpIt, User::op_iterator OpItE)
While OpIt points to a Value that is not an Instruction keep incrementing it.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition: STLExtras.h:329
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:420
bool isModSet(const ModRefInfo MRI)
Definition: ModRef.h:48
void sort(IteratorTy Start, IteratorTy End)
Definition: STLExtras.h:1664
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
ModRefInfo
Flags indicating whether a memory access modifies or references memory.
Definition: ModRef.h:27
@ ModRef
The access may reference and may modify the value stored in memory.
@ Other
Any other memory.
DWARFExpression::Operation Op
bool isRefSet(const ModRefInfo MRI)
Definition: ModRef.h:51
#define N