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
220 void setNextNode(MemDGNode *N) { NextMemN = N; }
221 void setPrevNode(MemDGNode *N) { PrevMemN = N; }
222 friend class DependencyGraph; // For setNextNode(), setPrevNode().
223
224public:
226 assert(isMemDepNodeCandidate(I) && "Expected Mem instruction!");
227 }
228 static bool classof(const DGNode *Other) {
229 return Other->SubclassID == DGNodeID::MemDGNode;
230 }
232 auto OpEndIt = I->op_end();
233 return PredIterator(skipNonInstr(I->op_begin(), OpEndIt), OpEndIt,
234 MemPreds.begin(), this, DAG);
235 }
237 return PredIterator(I->op_end(), I->op_end(), MemPreds.end(), this, DAG);
238 }
239 /// \Returns the previous Mem DGNode in instruction order.
240 MemDGNode *getPrevNode() const { return PrevMemN; }
241 /// \Returns the next Mem DGNode in instruction order.
242 MemDGNode *getNextNode() const { return NextMemN; }
243 /// Adds the mem dependency edge PredN->this. This also increments the
244 /// UnscheduledSuccs counter of the predecessor if this node has not been
245 /// scheduled.
246 void addMemPred(MemDGNode *PredN) {
247 [[maybe_unused]] auto Inserted = MemPreds.insert(PredN).second;
248 assert(Inserted && "PredN already exists!");
249 if (!Scheduled) {
250 ++PredN->UnscheduledSuccs;
251 }
252 }
253 /// \Returns true if there is a memory dependency N->this.
254 bool hasMemPred(DGNode *N) const {
255 if (auto *MN = dyn_cast<MemDGNode>(N))
256 return MemPreds.count(MN);
257 return false;
258 }
259 /// \Returns all memory dependency predecessors. Used by tests.
261 return make_range(MemPreds.begin(), MemPreds.end());
262 }
263#ifndef NDEBUG
264 virtual void print(raw_ostream &OS, bool PrintDeps = true) const override;
265#endif // NDEBUG
266};
267
268/// Convenience builders for a MemDGNode interval.
270public:
271 /// Scans the instruction chain in \p Intvl top-down, returning the top-most
272 /// MemDGNode, or nullptr.
274 const DependencyGraph &DAG);
275 /// Scans the instruction chain in \p Intvl bottom-up, returning the
276 /// bottom-most MemDGNode, or nullptr.
278 const DependencyGraph &DAG);
279 /// Given \p Instrs it finds their closest mem nodes in the interval and
280 /// returns the corresponding mem range. Note: BotN (or its neighboring mem
281 /// node) is included in the range.
283 DependencyGraph &DAG);
284 static Interval<MemDGNode> makeEmpty() { return {}; }
285};
286
288private:
290 /// The DAG spans across all instructions in this interval.
291 Interval<Instruction> DAGInterval;
292
293 Context *Ctx = nullptr;
294 std::optional<Context::CallbackID> CreateInstrCB;
295 std::optional<Context::CallbackID> EraseInstrCB;
296
297 std::unique_ptr<BatchAAResults> BatchAA;
298
299 enum class DependencyType {
300 ReadAfterWrite, ///> Memory dependency write -> read
301 WriteAfterWrite, ///> Memory dependency write -> write
302 WriteAfterRead, ///> Memory dependency read -> write
303 Control, ///> Control-related dependency, like with PHI/Terminator
304 Other, ///> Currently used for stack related instrs
305 None, ///> No memory/other dependency
306 };
307 /// \Returns the dependency type depending on whether instructions may
308 /// read/write memory or whether they are some specific opcode-related
309 /// restrictions.
310 /// Note: It does not check whether a memory dependency is actually correct,
311 /// as it won't call AA. Therefore it returns the worst-case dep type.
312 static DependencyType getRoughDepType(Instruction *FromI, Instruction *ToI);
313
314 // TODO: Implement AABudget.
315 /// \Returns true if there is a memory/other dependency \p SrcI->DstI.
316 bool alias(Instruction *SrcI, Instruction *DstI, DependencyType DepType);
317
318 bool hasDep(sandboxir::Instruction *SrcI, sandboxir::Instruction *DstI);
319
320 /// Go through all mem nodes in \p SrcScanRange and try to add dependencies to
321 /// \p DstN.
322 void scanAndAddDeps(MemDGNode &DstN, const Interval<MemDGNode> &SrcScanRange);
323
324 /// Sets the UnscheduledSuccs of all DGNodes in \p NewInterval based on
325 /// def-use edges.
326 void setDefUseUnscheduledSuccs(const Interval<Instruction> &NewInterval);
327
328 /// Create DAG nodes for instrs in \p NewInterval and update the MemNode
329 /// chain.
330 void createNewNodes(const Interval<Instruction> &NewInterval);
331
332 /// Helper for `notify*Instr()`. \Returns the first MemDGNode that comes
333 /// before \p N, including or excluding \p N based on \p IncludingN, or
334 /// nullptr if not found.
335 MemDGNode *getMemDGNodeBefore(DGNode *N, bool IncludingN) const;
336 /// Helper for `notifyMoveInstr()`. \Returns the first MemDGNode that comes
337 /// after \p N, including or excluding \p N based on \p IncludingN, or nullptr
338 /// if not found.
339 MemDGNode *getMemDGNodeAfter(DGNode *N, bool IncludingN) const;
340
341 /// Called by the callbacks when a new instruction \p I has been created.
342 void notifyCreateInstr(Instruction *I);
343 /// Called by the callbacks when instruction \p I is about to get
344 /// deleted.
345 void notifyEraseInstr(Instruction *I);
346
347public:
348 /// This constructor also registers callbacks.
350 : Ctx(&Ctx), BatchAA(std::make_unique<BatchAAResults>(AA)) {
351 CreateInstrCB = Ctx.registerCreateInstrCallback(
352 [this](Instruction *I) { notifyCreateInstr(I); });
353 EraseInstrCB = Ctx.registerEraseInstrCallback(
354 [this](Instruction *I) { notifyEraseInstr(I); });
355 }
357 if (CreateInstrCB)
358 Ctx->unregisterCreateInstrCallback(*CreateInstrCB);
359 if (EraseInstrCB)
360 Ctx->unregisterEraseInstrCallback(*EraseInstrCB);
361 }
362
364 auto It = InstrToNodeMap.find(I);
365 return It != InstrToNodeMap.end() ? It->second.get() : nullptr;
366 }
367 /// Like getNode() but returns nullptr if \p I is nullptr.
369 if (I == nullptr)
370 return nullptr;
371 return getNode(I);
372 }
374 auto [It, NotInMap] = InstrToNodeMap.try_emplace(I);
375 if (NotInMap) {
377 It->second = std::make_unique<MemDGNode>(I);
378 else
379 It->second = std::make_unique<DGNode>(I);
380 }
381 return It->second.get();
382 }
383 /// Build/extend the dependency graph such that it includes \p Instrs. Returns
384 /// the range of instructions added to the DAG.
386 /// \Returns the range of instructions included in the DAG.
387 Interval<Instruction> getInterval() const { return DAGInterval; }
388 void clear() {
389 InstrToNodeMap.clear();
390 DAGInterval = {};
391 }
392#ifndef NDEBUG
393 void print(raw_ostream &OS) const;
394 LLVM_DUMP_METHOD void dump() const;
395#endif // NDEBUG
396};
397
398} // namespace llvm::sandboxir
399
400#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:1313
#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
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
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 unregisterEraseInstrCallback(CallbackID ID)
Definition: Context.cpp:693
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.
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
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