LLVM 20.0.0git
DependencyGraph.h
Go to the documentation of this file.
1//===- DependencyGraph.h ----------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file declares the dependency graph used by the vectorizer's instruction
10// scheduler.
11//
12// The nodes of the graph are objects of the `DGNode` class. Each `DGNode`
13// object points to an instruction.
14// The edges between `DGNode`s are implicitly defined by an ordered set of
15// predecessor nodes, to save memory.
16// Finally the whole dependency graph is an object of the `DependencyGraph`
17// class, which also provides the API for creating/extending the graph from
18// input Sandbox IR.
19//
20//===----------------------------------------------------------------------===//
21
22#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_DEPENDENCYGRAPH_H
23#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_DEPENDENCYGRAPH_H
24
25#include "llvm/ADT/DenseMap.h"
31
32namespace llvm::sandboxir {
33
34class DependencyGraph;
35class MemDGNode;
36class SchedBundle;
37
38/// SubclassIDs for isa/dyn_cast etc.
39enum class DGNodeID {
40 DGNode,
42};
43
44class DGNode;
45class MemDGNode;
46class DependencyGraph;
47
48/// While OpIt points to a Value that is not an Instruction keep incrementing
49/// it. \Returns the first iterator that points to an Instruction, or end.
51 User::op_iterator OpItE) {
52 while (OpIt != OpItE && !isa<Instruction>((*OpIt).get()))
53 ++OpIt;
54 return OpIt;
55}
56
57/// Iterate over both def-use and mem dependencies.
62 DGNode *N = nullptr;
63 DependencyGraph *DAG = nullptr;
64
65 PredIterator(const User::op_iterator &OpIt, const User::op_iterator &OpItE,
67 DependencyGraph &DAG)
68 : OpIt(OpIt), OpItE(OpItE), MemIt(MemIt), N(N), DAG(&DAG) {}
69 PredIterator(const User::op_iterator &OpIt, const User::op_iterator &OpItE,
71 : OpIt(OpIt), OpItE(OpItE), N(N), DAG(&DAG) {}
72 friend class DGNode; // For constructor
73 friend class MemDGNode; // For constructor
74
75public:
76 using difference_type = std::ptrdiff_t;
77 using value_type = DGNode *;
80 using iterator_category = std::input_iterator_tag;
84 auto Copy = *this;
85 ++(*this);
86 return Copy;
87 }
88 bool operator==(const PredIterator &Other) const;
89 bool operator!=(const PredIterator &Other) const { return !(*this == Other); }
90};
91
92/// A DependencyGraph Node that points to an Instruction and contains memory
93/// dependency edges.
94class DGNode {
95protected:
97 // TODO: Use a PointerIntPair for SubclassID and I.
98 /// For isa/dyn_cast etc.
100 /// The number of unscheduled successors.
101 unsigned UnscheduledSuccs = 0;
102 /// This is true if this node has been scheduled.
103 bool Scheduled = false;
104 /// The scheduler bundle that this node belongs to.
105 SchedBundle *SB = nullptr;
106
107 void setSchedBundle(SchedBundle &SB) { this->SB = &SB; }
108 void clearSchedBundle() { this->SB = nullptr; }
109 friend class SchedBundle; // For setSchedBundle(), clearSchedBundle().
110
112 friend class MemDGNode; // For constructor.
113 friend class DependencyGraph; // For UnscheduledSuccs
114
115public:
117 assert(!isMemDepNodeCandidate(I) && "Expected Non-Mem instruction, ");
118 }
119 DGNode(const DGNode &Other) = delete;
120 virtual ~DGNode();
121 /// \Returns the number of unscheduled successors.
122 unsigned getNumUnscheduledSuccs() const { return UnscheduledSuccs; }
124 assert(UnscheduledSuccs > 0 && "Counting error!");
126 }
127 /// \Returns true if all dependent successors have been scheduled.
128 bool ready() const { return UnscheduledSuccs == 0; }
129 /// \Returns true if this node has been scheduled.
130 bool scheduled() const { return Scheduled; }
131 void setScheduled(bool NewVal) { Scheduled = NewVal; }
132 /// \Returns the scheduling bundle that this node belongs to, or nullptr.
133 SchedBundle *getSchedBundle() const { return SB; }
134 /// \Returns true if this is before \p Other in program order.
135 bool comesBefore(const DGNode *Other) { return I->comesBefore(Other->I); }
138 return PredIterator(skipNonInstr(I->op_begin(), I->op_end()), I->op_end(),
139 this, DAG);
140 }
142 return PredIterator(I->op_end(), I->op_end(), this, DAG);
143 }
145 return const_cast<DGNode *>(this)->preds_begin(DAG);
146 }
148 return const_cast<DGNode *>(this)->preds_end(DAG);
149 }
150 /// \Returns a range of DAG predecessors nodes. If this is a MemDGNode then
151 /// this will also include the memory dependency predecessors.
152 /// Please note that this can include the same node more than once, if for
153 /// example it's both a use-def predecessor and a mem dep predecessor.
155 return make_range(preds_begin(DAG), preds_end(DAG));
156 }
157
159 if (auto *II = dyn_cast<IntrinsicInst>(I)) {
160 auto IID = II->getIntrinsicID();
161 return IID == Intrinsic::stackrestore || IID == Intrinsic::stacksave;
162 }
163 return false;
164 }
165
166 /// \Returns true if intrinsic \p I touches memory. This is used by the
167 /// dependency graph.
169 auto IID = I->getIntrinsicID();
170 return IID != Intrinsic::sideeffect && IID != Intrinsic::pseudoprobe;
171 }
172
173 /// We consider \p I as a Memory Dependency Candidate instruction if it
174 /// reads/write memory or if it has side-effects. This is used by the
175 /// dependency graph.
178 return I->mayReadOrWriteMemory() &&
179 (!(II = dyn_cast<IntrinsicInst>(I)) || isMemIntrinsic(II));
180 }
181
182 /// \Returns true if \p I is fence like. It excludes non-mem intrinsics.
183 static bool isFenceLike(Instruction *I) {
185 return I->isFenceLike() &&
186 (!(II = dyn_cast<IntrinsicInst>(I)) || isMemIntrinsic(II));
187 }
188
189 /// \Returns true if \p I is a memory dependency candidate instruction.
191 AllocaInst *Alloca;
192 return isMemDepCandidate(I) ||
193 ((Alloca = dyn_cast<AllocaInst>(I)) &&
194 Alloca->isUsedWithInAlloca()) ||
196 }
197
198 Instruction *getInstruction() const { return I; }
199
200#ifndef NDEBUG
201 virtual void print(raw_ostream &OS, bool PrintDeps = true) const;
203 N.print(OS);
204 return OS;
205 }
206 LLVM_DUMP_METHOD void dump() const;
207#endif // NDEBUG
208};
209
210/// A DependencyGraph Node for instructions that may read/write memory, or have
211/// some ordering constraints, like with stacksave/stackrestore and
212/// alloca/inalloca.
213class MemDGNode final : public DGNode {
214 MemDGNode *PrevMemN = nullptr;
215 MemDGNode *NextMemN = nullptr;
216 /// Memory predecessors.
217 DenseSet<MemDGNode *> MemPreds;
218 friend class PredIterator; // For MemPreds.
219 /// Creates both edges: this<->N.
220 void setNextNode(MemDGNode *N) {
221 assert(N != this && "About to point to self!");
222 NextMemN = N;
223 if (NextMemN != nullptr)
224 NextMemN->PrevMemN = this;
225 }
226 /// Creates both edges: N<->this.
227 void setPrevNode(MemDGNode *N) {
228 assert(N != this && "About to point to self!");
229 PrevMemN = N;
230 if (PrevMemN != nullptr)
231 PrevMemN->NextMemN = this;
232 }
233 friend class DependencyGraph; // For setNextNode(), setPrevNode().
234 void detachFromChain() {
235 if (PrevMemN != nullptr)
236 PrevMemN->NextMemN = NextMemN;
237 if (NextMemN != nullptr)
238 NextMemN->PrevMemN = PrevMemN;
239 PrevMemN = nullptr;
240 NextMemN = nullptr;
241 }
242
243public:
245 assert(isMemDepNodeCandidate(I) && "Expected Mem instruction!");
246 }
247 static bool classof(const DGNode *Other) {
248 return Other->SubclassID == DGNodeID::MemDGNode;
249 }
251 auto OpEndIt = I->op_end();
252 return PredIterator(skipNonInstr(I->op_begin(), OpEndIt), OpEndIt,
253 MemPreds.begin(), this, DAG);
254 }
256 return PredIterator(I->op_end(), I->op_end(), MemPreds.end(), this, DAG);
257 }
258 /// \Returns the previous Mem DGNode in instruction order.
259 MemDGNode *getPrevNode() const { return PrevMemN; }
260 /// \Returns the next Mem DGNode in instruction order.
261 MemDGNode *getNextNode() const { return NextMemN; }
262 /// Adds the mem dependency edge PredN->this. This also increments the
263 /// UnscheduledSuccs counter of the predecessor if this node has not been
264 /// scheduled.
265 void addMemPred(MemDGNode *PredN) {
266 [[maybe_unused]] auto Inserted = MemPreds.insert(PredN).second;
267 assert(Inserted && "PredN already exists!");
268 if (!Scheduled) {
269 ++PredN->UnscheduledSuccs;
270 }
271 }
272 /// \Returns true if there is a memory dependency N->this.
273 bool hasMemPred(DGNode *N) const {
274 if (auto *MN = dyn_cast<MemDGNode>(N))
275 return MemPreds.count(MN);
276 return false;
277 }
278 /// \Returns all memory dependency predecessors. Used by tests.
280 return make_range(MemPreds.begin(), MemPreds.end());
281 }
282#ifndef NDEBUG
283 virtual void print(raw_ostream &OS, bool PrintDeps = true) const override;
284#endif // NDEBUG
285};
286
287/// Convenience builders for a MemDGNode interval.
289public:
290 /// Scans the instruction chain in \p Intvl top-down, returning the top-most
291 /// MemDGNode, or nullptr.
293 const DependencyGraph &DAG);
294 /// Scans the instruction chain in \p Intvl bottom-up, returning the
295 /// bottom-most MemDGNode, or nullptr.
297 const DependencyGraph &DAG);
298 /// Given \p Instrs it finds their closest mem nodes in the interval and
299 /// returns the corresponding mem range. Note: BotN (or its neighboring mem
300 /// node) is included in the range.
302 DependencyGraph &DAG);
303 static Interval<MemDGNode> makeEmpty() { return {}; }
304};
305
307private:
309 /// The DAG spans across all instructions in this interval.
310 Interval<Instruction> DAGInterval;
311
312 Context *Ctx = nullptr;
313 std::optional<Context::CallbackID> CreateInstrCB;
314 std::optional<Context::CallbackID> EraseInstrCB;
315 std::optional<Context::CallbackID> MoveInstrCB;
316
317 std::unique_ptr<BatchAAResults> BatchAA;
318
319 enum class DependencyType {
320 ReadAfterWrite, ///> Memory dependency write -> read
321 WriteAfterWrite, ///> Memory dependency write -> write
322 WriteAfterRead, ///> Memory dependency read -> write
323 Control, ///> Control-related dependency, like with PHI/Terminator
324 Other, ///> Currently used for stack related instrs
325 None, ///> No memory/other dependency
326 };
327 /// \Returns the dependency type depending on whether instructions may
328 /// read/write memory or whether they are some specific opcode-related
329 /// restrictions.
330 /// Note: It does not check whether a memory dependency is actually correct,
331 /// as it won't call AA. Therefore it returns the worst-case dep type.
332 static DependencyType getRoughDepType(Instruction *FromI, Instruction *ToI);
333
334 // TODO: Implement AABudget.
335 /// \Returns true if there is a memory/other dependency \p SrcI->DstI.
336 bool alias(Instruction *SrcI, Instruction *DstI, DependencyType DepType);
337
338 bool hasDep(sandboxir::Instruction *SrcI, sandboxir::Instruction *DstI);
339
340 /// Go through all mem nodes in \p SrcScanRange and try to add dependencies to
341 /// \p DstN.
342 void scanAndAddDeps(MemDGNode &DstN, const Interval<MemDGNode> &SrcScanRange);
343
344 /// Sets the UnscheduledSuccs of all DGNodes in \p NewInterval based on
345 /// def-use edges.
346 void setDefUseUnscheduledSuccs(const Interval<Instruction> &NewInterval);
347
348 /// Create DAG nodes for instrs in \p NewInterval and update the MemNode
349 /// chain.
350 void createNewNodes(const Interval<Instruction> &NewInterval);
351
352 /// Helper for `notify*Instr()`. \Returns the first MemDGNode that comes
353 /// before \p N, skipping \p SkipN, including or excluding \p N based on
354 /// \p IncludingN, or nullptr if not found.
355 MemDGNode *getMemDGNodeBefore(DGNode *N, bool IncludingN,
356 MemDGNode *SkipN = nullptr) const;
357 /// Helper for `notifyMoveInstr()`. \Returns the first MemDGNode that comes
358 /// after \p N, skipping \p SkipN, including or excluding \p N based on \p
359 /// IncludingN, or nullptr if not found.
360 MemDGNode *getMemDGNodeAfter(DGNode *N, bool IncludingN,
361 MemDGNode *SkipN = nullptr) const;
362
363 /// Called by the callbacks when a new instruction \p I has been created.
364 void notifyCreateInstr(Instruction *I);
365 /// Called by the callbacks when instruction \p I is about to get
366 /// deleted.
367 void notifyEraseInstr(Instruction *I);
368 /// Called by the callbacks when instruction \p I is about to be moved to
369 /// \p To.
370 void notifyMoveInstr(Instruction *I, const BBIterator &To);
371
372public:
373 /// This constructor also registers callbacks.
375 : Ctx(&Ctx), BatchAA(std::make_unique<BatchAAResults>(AA)) {
376 CreateInstrCB = Ctx.registerCreateInstrCallback(
377 [this](Instruction *I) { notifyCreateInstr(I); });
378 EraseInstrCB = Ctx.registerEraseInstrCallback(
379 [this](Instruction *I) { notifyEraseInstr(I); });
380 MoveInstrCB = Ctx.registerMoveInstrCallback(
381 [this](Instruction *I, const BBIterator &To) {
382 notifyMoveInstr(I, To);
383 });
384 }
386 if (CreateInstrCB)
387 Ctx->unregisterCreateInstrCallback(*CreateInstrCB);
388 if (EraseInstrCB)
389 Ctx->unregisterEraseInstrCallback(*EraseInstrCB);
390 if (MoveInstrCB)
391 Ctx->unregisterMoveInstrCallback(*MoveInstrCB);
392 }
393
395 auto It = InstrToNodeMap.find(I);
396 return It != InstrToNodeMap.end() ? It->second.get() : nullptr;
397 }
398 /// Like getNode() but returns nullptr if \p I is nullptr.
400 if (I == nullptr)
401 return nullptr;
402 return getNode(I);
403 }
405 auto [It, NotInMap] = InstrToNodeMap.try_emplace(I);
406 if (NotInMap) {
408 It->second = std::make_unique<MemDGNode>(I);
409 else
410 It->second = std::make_unique<DGNode>(I);
411 }
412 return It->second.get();
413 }
414 /// Build/extend the dependency graph such that it includes \p Instrs. Returns
415 /// the range of instructions added to the DAG.
417 /// \Returns the range of instructions included in the DAG.
418 Interval<Instruction> getInterval() const { return DAGInterval; }
419 void clear() {
420 InstrToNodeMap.clear();
421 DAGInterval = {};
422 }
423#ifndef NDEBUG
424 /// \Returns true if the DAG's state is clear. Used in assertions.
425 bool empty() const {
426 bool IsEmpty = InstrToNodeMap.empty();
427 assert(IsEmpty == DAGInterval.empty() &&
428 "Interval and InstrToNodeMap out of sync!");
429 return IsEmpty;
430 }
431 void print(raw_ostream &OS) const;
432 LLVM_DUMP_METHOD void dump() const;
433#endif // NDEBUG
434};
435
436} // namespace llvm::sandboxir
437
438#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_DEPENDENCYGRAPH_H
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Definition: Compiler.h:622
This file defines the DenseMap class.
std::optional< std::vector< StOtherPiece > > Other
Definition: ELFYAML.cpp:1315
#define I(x, y, z)
Definition: MD5.cpp:58
uint64_t IntrinsicInst * II
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
This class is a wrapper over an AAResults, and it is intended to be used only when there are no IR ch...
Represent a node in the directed graph.
Definition: DirectedGraph.h:73
iterator find(const_arg_type_t< KeyT > Val)
Definition: DenseMap.h:156
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition: DenseMap.h:226
bool empty() const
Definition: DenseMap.h:98
iterator end()
Definition: DenseMap.h:84
Implements a dense probed hash-table based set.
Definition: DenseSet.h:278
std::pair< iterator, bool > insert(const ValueT &V)
Definition: DenseSet.h:213
size_type count(const_arg_type_t< ValueT > V) const
Return 1 if the specified key is in the set, 0 otherwise.
Definition: DenseSet.h:95
A range adaptor for a pair of iterators.
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
bool isUsedWithInAlloca() const
Return true if this alloca is used as an inalloca argument to a call.
Definition: Instruction.h:2241
Iterator for Instructions in a `BasicBlock.
Definition: BasicBlock.h:23
CallbackID registerCreateInstrCallback(CreateInstrCallback CB)
Register a callback that gets called right after a SandboxIR instruction is created.
Definition: Context.cpp:700
void unregisterCreateInstrCallback(CallbackID ID)
Definition: Context.cpp:707
void unregisterMoveInstrCallback(CallbackID ID)
Definition: Context.cpp:720
void unregisterEraseInstrCallback(CallbackID ID)
Definition: Context.cpp:693
CallbackID registerMoveInstrCallback(MoveInstrCallback CB)
Register a callback that gets called when a SandboxIR instruction is about to be moved.
Definition: Context.cpp:713
CallbackID registerEraseInstrCallback(EraseInstrCallback CB)
Register a callback that gets called when a SandboxIR instruction is about to be removed from its par...
Definition: Context.cpp:686
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...
virtual iterator preds_end(DependencyGraph &DAG)
static bool isMemIntrinsic(IntrinsicInst *I)
\Returns true if intrinsic I touches memory.
iterator preds_begin(DependencyGraph &DAG) const
DGNode(Instruction *I, DGNodeID ID)
unsigned getNumUnscheduledSuccs() const
\Returns the number of unscheduled successors.
void setSchedBundle(SchedBundle &SB)
bool scheduled() const
\Returns true if this node has been scheduled.
unsigned UnscheduledSuccs
The number of unscheduled successors.
void setScheduled(bool NewVal)
bool ready() const
\Returns true if all dependent successors have been scheduled.
iterator_range< iterator > preds(DependencyGraph &DAG) const
\Returns a range of DAG predecessors nodes.
iterator preds_end(DependencyGraph &DAG) const
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.
SchedBundle * getSchedBundle() const
\Returns the scheduling bundle that this node belongs to, or nullptr.
DGNodeID SubclassID
For isa/dyn_cast etc.
DGNode(const DGNode &Other)=delete
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)
bool comesBefore(const DGNode *Other)
\Returns true if this is before Other in program order.
friend raw_ostream & operator<<(raw_ostream &OS, DGNode &N)
virtual iterator preds_begin(DependencyGraph &DAG)
Interval< Instruction > getInterval() const
\Returns the range of instructions included in the DAG.
bool empty() const
\Returns true if the DAG's state is clear. Used in assertions.
LLVM_DUMP_METHOD void dump() const
DGNode * getNode(Instruction *I) const
DependencyGraph(AAResults &AA, Context &Ctx)
This constructor also registers callbacks.
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
bool mayReadOrWriteMemory() const
Definition: Instruction.h:339
bool comesBefore(const Instruction *Other) const
Given an instruction Other in the same basic block as this instruction, return true if this instructi...
Definition: Instruction.h:214
bool empty() const
Definition: Interval.h:99
Convenience builders for a MemDGNode interval.
static MemDGNode * getBotMemDGNode(const Interval< Instruction > &Intvl, const DependencyGraph &DAG)
Scans the instruction chain in Intvl bottom-up, returning the bottom-most MemDGNode,...
static Interval< MemDGNode > makeEmpty()
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...
iterator preds_end(DependencyGraph &DAG) override
iterator preds_begin(DependencyGraph &DAG) override
bool hasMemPred(DGNode *N) const
\Returns true if there is a memory dependency N->this.
static bool classof(const DGNode *Other)
iterator_range< DenseSet< MemDGNode * >::const_iterator > memPreds() const
\Returns all memory dependency predecessors. Used by tests.
MemDGNode * getNextNode() const
\Returns the next Mem DGNode in instruction order.
virtual void print(raw_ostream &OS, bool PrintDeps=true) const override
MemDGNode * getPrevNode() const
\Returns the previous Mem DGNode in instruction order.
void addMemPred(MemDGNode *PredN)
Adds the mem dependency edge PredN->this.
Iterator for the Use edges of a User's operands.
Definition: User.h:23
Iterate over both def-use and mem dependencies.
bool operator!=(const PredIterator &Other) const
std::input_iterator_tag iterator_category
The nodes that need to be scheduled back-to-back in a single scheduling cycle form a SchedBundle.
Definition: Scheduler.h:65
virtual op_iterator op_begin()
Definition: User.h:102
virtual op_iterator op_end()
Definition: User.h:106
This provides a very simple, boring adaptor for a begin and end iterator into a range type.
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.
DGNodeID
SubclassIDs for isa/dyn_cast etc.
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
@ None
Definition: CodeGenData.h:106
@ Other
Any other memory.
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858
#define N