LLVM 20.0.0git
SPIRVConvergenceRegionAnalysis.cpp
Go to the documentation of this file.
1//===- ConvergenceRegionAnalysis.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// The analysis determines the convergence region for each basic block of
10// the module, and provides a tree-like structure describing the region
11// hierarchy.
12//
13//===----------------------------------------------------------------------===//
14
17#include "llvm/IR/Dominators.h"
21#include <optional>
22#include <queue>
23
24#define DEBUG_TYPE "spirv-convergence-region-analysis"
25
26using namespace llvm;
27
28namespace llvm {
30} // namespace llvm
31
33 "convergence-region",
34 "SPIRV convergence regions analysis", true, true)
39 "convergence-region", "SPIRV convergence regions analysis",
41
42namespace llvm {
43namespace SPIRV {
44namespace {
45
46template <typename BasicBlockType, typename IntrinsicInstType>
47std::optional<IntrinsicInstType *>
48getConvergenceTokenInternal(BasicBlockType *BB) {
49 static_assert(std::is_const_v<IntrinsicInstType> ==
50 std::is_const_v<BasicBlockType>,
51 "Constness must match between input and output.");
52 static_assert(std::is_same_v<BasicBlock, std::remove_const_t<BasicBlockType>>,
53 "Input must be a basic block.");
54 static_assert(
55 std::is_same_v<IntrinsicInst, std::remove_const_t<IntrinsicInstType>>,
56 "Output type must be an intrinsic instruction.");
57
58 for (auto &I : *BB) {
59 if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
60 switch (II->getIntrinsicID()) {
61 case Intrinsic::experimental_convergence_entry:
62 case Intrinsic::experimental_convergence_loop:
63 return II;
64 case Intrinsic::experimental_convergence_anchor: {
65 auto Bundle = II->getOperandBundle(LLVMContext::OB_convergencectrl);
66 assert(Bundle->Inputs.size() == 1 &&
67 Bundle->Inputs[0]->getType()->isTokenTy());
68 auto TII = dyn_cast<IntrinsicInst>(Bundle->Inputs[0].get());
69 assert(TII != nullptr);
70 return TII;
71 }
72 }
73 }
74
75 if (auto *CI = dyn_cast<CallInst>(&I)) {
76 auto OB = CI->getOperandBundle(LLVMContext::OB_convergencectrl);
77 if (!OB.has_value())
78 continue;
79 return dyn_cast<IntrinsicInst>(OB.value().Inputs[0]);
80 }
81 }
82
83 return std::nullopt;
84}
85
86// Given a ConvergenceRegion tree with |Start| as its root, finds the smallest
87// region |Entry| belongs to. If |Entry| does not belong to the region defined
88// by |Start|, this function returns |nullptr|.
89ConvergenceRegion *findParentRegion(ConvergenceRegion *Start,
90 BasicBlock *Entry) {
91 ConvergenceRegion *Candidate = nullptr;
92 ConvergenceRegion *NextCandidate = Start;
93
94 while (Candidate != NextCandidate && NextCandidate != nullptr) {
95 Candidate = NextCandidate;
96 NextCandidate = nullptr;
97
98 // End of the search, we can return.
99 if (Candidate->Children.size() == 0)
100 return Candidate;
101
102 for (auto *Child : Candidate->Children) {
103 if (Child->Blocks.count(Entry) != 0) {
104 NextCandidate = Child;
105 break;
106 }
107 }
108 }
109
110 return Candidate;
111}
112
113} // anonymous namespace
114
115std::optional<IntrinsicInst *> getConvergenceToken(BasicBlock *BB) {
116 return getConvergenceTokenInternal<BasicBlock, IntrinsicInst>(BB);
117}
118
119std::optional<const IntrinsicInst *> getConvergenceToken(const BasicBlock *BB) {
120 return getConvergenceTokenInternal<const BasicBlock, const IntrinsicInst>(BB);
121}
122
124 Function &F)
125 : DT(DT), LI(LI), Parent(nullptr) {
126 Entry = &F.getEntryBlock();
128 for (auto &B : F) {
129 Blocks.insert(&B);
130 if (isa<ReturnInst>(B.getTerminator()))
131 Exits.insert(&B);
132 }
133}
134
136 DominatorTree &DT, LoopInfo &LI,
137 std::optional<IntrinsicInst *> ConvergenceToken, BasicBlock *Entry,
139 : DT(DT), LI(LI), ConvergenceToken(ConvergenceToken), Entry(Entry),
140 Exits(std::move(Exits)), Blocks(std::move(Blocks)) {
141 for ([[maybe_unused]] auto *BB : this->Exits)
142 assert(this->Blocks.count(BB) != 0);
143 assert(this->Blocks.count(this->Entry) != 0);
144}
145
147 // Parent memory is owned by the parent.
148 Parent = nullptr;
149 for (auto *Child : Children) {
150 Child->releaseMemory();
151 delete Child;
152 }
153 Children.resize(0);
154}
155
156void ConvergenceRegion::dump(const unsigned IndentSize) const {
157 const std::string Indent(IndentSize, '\t');
158 dbgs() << Indent << this << ": {\n";
159 dbgs() << Indent << " Parent: " << Parent << "\n";
160
161 if (ConvergenceToken.value_or(nullptr)) {
162 dbgs() << Indent
163 << " ConvergenceToken: " << ConvergenceToken.value()->getName()
164 << "\n";
165 }
166
167 if (Entry->getName() != "")
168 dbgs() << Indent << " Entry: " << Entry->getName() << "\n";
169 else
170 dbgs() << Indent << " Entry: " << Entry << "\n";
171
172 dbgs() << Indent << " Exits: { ";
173 for (const auto &Exit : Exits) {
174 if (Exit->getName() != "")
175 dbgs() << Exit->getName() << ", ";
176 else
177 dbgs() << Exit << ", ";
178 }
179 dbgs() << " }\n";
180
181 dbgs() << Indent << " Blocks: { ";
182 for (const auto &Block : Blocks) {
183 if (Block->getName() != "")
184 dbgs() << Block->getName() << ", ";
185 else
186 dbgs() << Block << ", ";
187 }
188 dbgs() << " }\n";
189
190 dbgs() << Indent << " Children: {\n";
191 for (const auto Child : Children)
192 Child->dump(IndentSize + 2);
193 dbgs() << Indent << " }\n";
194
195 dbgs() << Indent << "}\n";
196}
197
199
200public:
202 : DT(DT), LI(LI), F(F) {}
203
204private:
205 bool isBackEdge(const BasicBlock *From, const BasicBlock *To) const {
206 if (From == To)
207 return true;
208
209 // We only handle loop in the simplified form. This means:
210 // - a single back-edge, a single latch.
211 // - meaning the back-edge target can only be the loop header.
212 // - meaning the From can only be the loop latch.
213 if (!LI.isLoopHeader(To))
214 return false;
215
216 auto *L = LI.getLoopFor(To);
217 if (L->contains(From) && L->isLoopLatch(From))
218 return true;
219
220 return false;
221 }
222
223 std::unordered_set<BasicBlock *>
224 findPathsToMatch(LoopInfo &LI, BasicBlock *From,
225 std::function<bool(const BasicBlock *)> isMatch) const {
226 std::unordered_set<BasicBlock *> Output;
227
228 if (isMatch(From))
229 Output.insert(From);
230
231 auto *Terminator = From->getTerminator();
232 for (unsigned i = 0; i < Terminator->getNumSuccessors(); ++i) {
233 auto *To = Terminator->getSuccessor(i);
234 // Ignore back edges.
235 if (isBackEdge(From, To))
236 continue;
237
238 auto ChildSet = findPathsToMatch(LI, To, isMatch);
239 if (ChildSet.size() == 0)
240 continue;
241
242 Output.insert(ChildSet.begin(), ChildSet.end());
243 Output.insert(From);
244 if (LI.isLoopHeader(From)) {
245 auto *L = LI.getLoopFor(From);
246 for (auto *BB : L->getBlocks()) {
247 Output.insert(BB);
248 }
249 }
250 }
251
252 return Output;
253 }
254
256 findExitNodes(const SmallPtrSetImpl<BasicBlock *> &RegionBlocks) {
258
259 for (auto *B : RegionBlocks) {
260 auto *Terminator = B->getTerminator();
261 for (unsigned i = 0; i < Terminator->getNumSuccessors(); ++i) {
262 auto *Child = Terminator->getSuccessor(i);
263 if (RegionBlocks.count(Child) == 0)
264 Exits.insert(B);
265 }
266 }
267
268 return Exits;
269 }
270
271public:
273 ConvergenceRegion *TopLevelRegion = new ConvergenceRegion(DT, LI, F);
274 std::queue<Loop *> ToProcess;
275 for (auto *L : LI.getLoopsInPreorder())
276 ToProcess.push(L);
277
278 while (ToProcess.size() != 0) {
279 auto *L = ToProcess.front();
280 ToProcess.pop();
281
282 auto CT = getConvergenceToken(L->getHeader());
283 SmallPtrSet<BasicBlock *, 8> RegionBlocks(L->block_begin(),
284 L->block_end());
286 L->getExitingBlocks(LoopExits);
287 if (CT.has_value()) {
288 for (auto *Exit : LoopExits) {
289 auto N = findPathsToMatch(LI, Exit, [&CT](const BasicBlock *block) {
290 auto Token = getConvergenceToken(block);
291 if (Token == std::nullopt)
292 return false;
293 return Token.value() == CT.value();
294 });
295 RegionBlocks.insert(N.begin(), N.end());
296 }
297 }
298
299 auto RegionExits = findExitNodes(RegionBlocks);
301 DT, LI, CT, L->getHeader(), std::move(RegionBlocks),
302 std::move(RegionExits));
303 Region->Parent = findParentRegion(TopLevelRegion, Region->Entry);
304 assert(Region->Parent != nullptr && "This is impossible.");
305 Region->Parent->Children.push_back(Region);
306 }
307
308 return ConvergenceRegionInfo(TopLevelRegion);
309 }
310
311private:
312 DominatorTree &DT;
313 LoopInfo &LI;
314 Function &F;
315};
316
318 LoopInfo &LI) {
319 ConvergenceRegionAnalyzer Analyzer(F, DT, LI);
320 return Analyzer.analyze();
321}
322
323} // namespace SPIRV
324
326
329 : FunctionPass(ID) {}
330
332 DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
333 LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
334
335 CRI = SPIRV::getConvergenceRegions(F, DT, LI);
336 // Nothing was modified.
337 return false;
338}
339
342 Result CRI;
343 auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
344 auto &LI = AM.getResult<LoopAnalysis>(F);
345 CRI = SPIRV::getConvergenceRegions(F, DT, LI);
346 return CRI;
347}
348
349AnalysisKey SPIRVConvergenceRegionAnalysis::Key;
350
351} // namespace llvm
BlockVerifier::State From
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
DenseMap< Block *, BlockRelaxAux > Blocks
Definition: ELF_riscv.cpp:507
const HexagonInstrInfo * TII
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition: PassSupport.h:55
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:57
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:52
regions
Definition: RegionInfo.cpp:168
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
convergence region
convergence SPIRV convergence regions true
convergence SPIRV convergence regions analysis
spirv structurize SPIRV
unify loop Fixup each natural loop to have a single exit block
A container for analyses that lazily runs them and caches their results.
Definition: PassManager.h:253
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Definition: PassManager.h:410
LLVM Basic Block Representation.
Definition: BasicBlock.h:61
Analysis pass which computes a DominatorTree.
Definition: Dominators.h:279
Legacy analysis pass which computes a DominatorTree.
Definition: Dominators.h:317
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition: Dominators.h:162
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:310
Analysis pass that exposes the LoopInfo for a function.
Definition: LoopInfo.h:566
bool isLoopHeader(const BlockT *BB) const
SmallVector< LoopT *, 4 > getLoopsInPreorder() const
Return all of the loops in the function in preorder across the loop nests, with siblings in forward p...
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
The legacy pass manager's analysis pass to compute loop information.
Definition: LoopInfo.h:593
PassRegistry - This class manages the registration and intitialization of the pass subsystem as appli...
Definition: PassRegistry.h:37
bool runOnFunction(Function &F) override
runOnFunction - Virtual method overriden by subclasses to do the per-function processing of the pass.
Result run(Function &F, FunctionAnalysisManager &AM)
ConvergenceRegionAnalyzer(Function &F, DominatorTree &DT, LoopInfo &LI)
SmallVector< ConvergenceRegion * > Children
SmallPtrSet< BasicBlock *, 2 > Exits
std::optional< IntrinsicInst * > ConvergenceToken
void dump(const unsigned IndentSize=0) const
ConvergenceRegion(DominatorTree &DT, LoopInfo &LI, Function &F)
SmallPtrSet< BasicBlock *, 8 > Blocks
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
Definition: SmallPtrSet.h:363
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
Definition: SmallPtrSet.h:384
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
Definition: SmallPtrSet.h:519
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
StringRef getName() const
Return a constant reference to the value's name.
Definition: Value.cpp:309
ConvergenceRegionInfo getConvergenceRegions(Function &F, DominatorTree &DT, LoopInfo &LI)
std::optional< IntrinsicInst * > getConvergenceToken(BasicBlock *BB)
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1873
void initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PassRegistry &)
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858
#define N
A special type used by analysis passes to provide an address that identifies that particular analysis...
Definition: Analysis.h:28