LLVM 24.0.0git
GenericUniformityImpl.h
Go to the documentation of this file.
1//===- GenericUniformityImpl.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 template implementation resides in a separate file so that it
10// does not get injected into every .cpp file that includes the
11// generic header.
12//
13// DO NOT INCLUDE THIS FILE WHEN MERELY USING UNIFORMITYINFO.
14//
15// This file should only be included by files that implement a
16// specialization of the relvant templates. Currently these are:
17// - UniformityAnalysis.cpp
18// - MachineUniformityAnalysis.cpp
19//
20// Note: The DEBUG_TYPE macro should be defined before using this
21// file so that any use of LLVM_DEBUG is associated with the
22// including file rather than this file.
23//
24//===----------------------------------------------------------------------===//
25///
26/// \file
27/// \brief Implementation of uniformity analysis.
28///
29/// The algorithm is a fixed point iteration that starts with the assumption
30/// that all control flow and all values are uniform. Starting from sources of
31/// divergence (whose discovery must be implemented by a CFG- or even
32/// target-specific derived class), divergence of values is propagated from
33/// definition to uses in a straight-forward way. The main complexity lies in
34/// the propagation of the impact of divergent control flow on the divergence of
35/// values (sync dependencies).
36///
37/// NOTE: In general, no interface exists for a transform to update
38/// (Machine)UniformityInfo. Additionally, (Machine)CycleAnalysis is a
39/// transitive dependence, but it also does not provide an interface for
40/// updating itself. Given that, transforms should not preserve uniformity in
41/// their getAnalysisUsage() callback.
42///
43//===----------------------------------------------------------------------===//
44
45#ifndef LLVM_ADT_GENERICUNIFORMITYIMPL_H
46#define LLVM_ADT_GENERICUNIFORMITYIMPL_H
47
49
50#include "llvm/ADT/DenseSet.h"
51#include "llvm/ADT/STLExtras.h"
52#include "llvm/ADT/SetVector.h"
57
58#define DEBUG_TYPE "uniformity"
59
60namespace llvm {
61
62// Forward decl from llvm/CodeGen/MachineInstr.h
63class MachineInstr;
64
65/// Construct a specially modified post-order traversal of cycles.
66///
67/// The ModifiedPO is contructed using a virtually modified CFG as follows:
68///
69/// 1. The successors of pre-entry nodes (predecessors of an cycle
70/// entry that are outside the cycle) are replaced by the
71/// successors of the successors of the header.
72/// 2. Successors of the cycle header are replaced by the exit blocks
73/// of the cycle.
74///
75/// Effectively, we produce a depth-first numbering with the following
76/// properties:
77///
78/// 1. Nodes after a cycle are numbered earlier than the cycle header.
79/// 2. The header is numbered earlier than the nodes in the cycle.
80/// 3. The numbering of the nodes within the cycle forms an interval
81/// starting with the header.
82///
83/// Effectively, the virtual modification arranges the nodes in a
84/// cycle as a DAG with the header as the sole leaf, and successors of
85/// the header as the roots. A reverse traversal of this numbering has
86/// the following invariant on the unmodified original CFG:
87///
88/// Each node is visited after all its predecessors, except if that
89/// predecessor is the cycle header.
90///
91template <typename ContextT> class ModifiedPostOrder {
92public:
93 using BlockT = typename ContextT::BlockT;
94 using FunctionT = typename ContextT::FunctionT;
95 using DominatorTreeT = typename ContextT::DominatorTreeT;
96
98 using const_iterator = typename std::vector<BlockT *>::const_iterator;
99
100 ModifiedPostOrder(const ContextT &C) : Context(C) {}
101
102 bool empty() const { return Order.empty(); }
103 size_t size() const { return Order.size(); }
104
105 void clear() { Order.clear(); }
106 void compute(const CycleInfoT &CI);
107
108 unsigned count(BlockT *BB) const {
109 unsigned Num = GraphTraits<BlockT *>::getNumber(BB);
110 return Num < POIndex.size() && POIndex[Num] != InvalidIndex;
111 }
112 const BlockT *operator[](size_t Idx) const { return Order[Idx]; }
113
114 void appendBlock(const BlockT &BB, bool IsReducibleCycleHeader = false) {
115 unsigned Num = GraphTraits<const BlockT *>::getNumber(&BB);
116 POIndex[Num] = Order.size();
117 Order.push_back(&BB);
118 LLVM_DEBUG(dbgs() << "ModifiedPO(" << POIndex[Num]
119 << "): " << Context.print(&BB) << "\n");
120 if (IsReducibleCycleHeader)
121 ReducibleCycleHeaders.insert(&BB);
122 }
123
124 unsigned getIndex(const BlockT *BB) const {
126 assert(Num < POIndex.size() && POIndex[Num] != InvalidIndex);
127 return POIndex[Num];
128 }
129
130 bool isReducibleCycleHeader(const BlockT *BB) const {
131 return ReducibleCycleHeaders.contains(BB);
132 }
133
134private:
135 static constexpr unsigned InvalidIndex = -1u;
136
138 SmallVector<unsigned> POIndex;
139 SmallPtrSet<const BlockT *, 32> ReducibleCycleHeaders;
140 const ContextT &Context;
141
142 void computeCyclePO(const CycleInfoT &CI, CycleRef C,
144
145 void computeStackPO(SmallVectorImpl<const BlockT *> &Stack,
146 const CycleInfoT &CI, CycleRef C,
148};
149
150template <typename> class DivergencePropagator;
151
152/// \class GenericSyncDependenceAnalysis
153///
154/// \brief Locate join blocks for disjoint paths starting at a divergent branch.
155///
156/// An analysis per divergent branch that returns the set of basic
157/// blocks whose phi nodes become divergent due to divergent control.
158/// These are the blocks that are reachable by two disjoint paths from
159/// the branch, or cycle exits reachable along a path that is disjoint
160/// from a path to the cycle latch.
161
162// --- Above line is not a doxygen comment; intentionally left blank ---
163//
164// Originally implemented in SyncDependenceAnalysis.cpp for DivergenceAnalysis.
165//
166// The SyncDependenceAnalysis is used in the UniformityAnalysis to model
167// control-induced divergence in phi nodes.
168//
169// -- Reference --
170// The algorithm is an extension of Section 5 of
171//
172// An abstract interpretation for SPMD divergence
173// on reducible control flow graphs.
174// Julian Rosemann, Simon Moll and Sebastian Hack
175// POPL '21
176//
177//
178// -- Sync dependence --
179// Sync dependence characterizes the control flow aspect of the
180// propagation of branch divergence. For example,
181//
182// %cond = icmp slt i32 %tid, 10
183// br i1 %cond, label %then, label %else
184// then:
185// br label %merge
186// else:
187// br label %merge
188// merge:
189// %a = phi i32 [ 0, %then ], [ 1, %else ]
190//
191// Suppose %tid holds the thread ID. Although %a is not data dependent on %tid
192// because %tid is not on its use-def chains, %a is sync dependent on %tid
193// because the branch "br i1 %cond" depends on %tid and affects which value %a
194// is assigned to.
195//
196//
197// -- Reduction to SSA construction --
198// There are two disjoint paths from A to X, if a certain variant of SSA
199// construction places a phi node in X under the following set-up scheme.
200//
201// This variant of SSA construction ignores incoming undef values.
202// That is paths from the entry without a definition do not result in
203// phi nodes.
204//
205// entry
206// / \
207// A \
208// / \ Y
209// B C /
210// \ / \ /
211// D E
212// \ /
213// F
214//
215// Assume that A contains a divergent branch. We are interested
216// in the set of all blocks where each block is reachable from A
217// via two disjoint paths. This would be the set {D, F} in this
218// case.
219// To generally reduce this query to SSA construction we introduce
220// a virtual variable x and assign to x different values in each
221// successor block of A.
222//
223// entry
224// / \
225// A \
226// / \ Y
227// x = 0 x = 1 /
228// \ / \ /
229// D E
230// \ /
231// F
232//
233// Our flavor of SSA construction for x will construct the following
234//
235// entry
236// / \
237// A \
238// / \ Y
239// x0 = 0 x1 = 1 /
240// \ / \ /
241// x2 = phi E
242// \ /
243// x3 = phi
244//
245// The blocks D and F contain phi nodes and are thus each reachable
246// by two disjoint paths from A.
247//
248// -- Remarks --
249// * In case of cycle exits we need to check for temporal divergence.
250// To this end, we check whether the definition of x differs between the
251// cycle exit and the cycle header (_after_ SSA construction).
252//
253// * In the presence of irreducible control flow, the fixed point is
254// reached only after multiple iterations. This is because labels
255// reaching the header of a cycle must be repropagated through the
256// cycle. This is true even in a reducible cycle, since the labels
257// may have been produced by a nested irreducible cycle.
258//
259// * Note that SyncDependenceAnalysis is not concerned with the points
260// of convergence in an irreducible cycle. Its only purpose is to
261// identify join blocks. The "diverged entry" criterion is
262// separately applied on join blocks to determine if an entire
263// irreducible cycle is assumed to be divergent.
264//
265// * Relevant related work:
266// A simple algorithm for global data flow analysis problems.
267// Matthew S. Hecht and Jeffrey D. Ullman.
268// SIAM Journal on Computing, 4(4):519–532, December 1975.
269//
270template <typename ContextT> class GenericSyncDependenceAnalysis {
271public:
272 using BlockT = typename ContextT::BlockT;
273 using DominatorTreeT = typename ContextT::DominatorTreeT;
274 using FunctionT = typename ContextT::FunctionT;
275 using ValueRefT = typename ContextT::ValueRefT;
276 using InstructionT = typename ContextT::InstructionT;
277
279
282
283 /// Information discovered by the sync dependence analysis for each
284 /// divergent branch.
286 // Join points of diverged paths.
288 // Divergent cycle exits
290 };
291
293
294 GenericSyncDependenceAnalysis(const ContextT &Context,
295 const DominatorTreeT &DT, const CycleInfoT &CI);
296
297 /// \brief Computes divergent join points and cycle exits caused by branch
298 /// divergence in \p Term.
299 ///
300 /// This returns a pair of sets:
301 /// * The set of blocks which are reachable by disjoint paths from
302 /// \p Term.
303 /// * The set also contains cycle exits if there two disjoint paths:
304 /// one from \p Term to the cycle exit and another from \p Term to
305 /// the cycle header.
306 const DivergenceDescriptor &getJoinBlocks(const BlockT *DivTermBlock);
307
308private:
309 static inline DivergenceDescriptor EmptyDivergenceDesc;
310
311 ModifiedPO CyclePO;
312
313 const DominatorTreeT &DT;
314 const CycleInfoT &CI;
315
317 CachedControlDivDescs;
318};
319
320/// \brief Analysis that identifies uniform values in a data-parallel
321/// execution.
322///
323/// This analysis propagates divergence in a data-parallel context
324/// from sources of divergence to all users. It can be instantiated
325/// for an IR that provides a suitable SSAContext.
326template <typename ContextT> class GenericUniformityAnalysisImpl {
327public:
328 using BlockT = typename ContextT::BlockT;
329 using FunctionT = typename ContextT::FunctionT;
330 using ValueRefT = typename ContextT::ValueRefT;
331 using ConstValueRefT = typename ContextT::ConstValueRefT;
332 using UseT = typename ContextT::UseT;
333 using InstructionT = typename ContextT::InstructionT;
334 using DominatorTreeT = typename ContextT::DominatorTreeT;
335
337
340 typename SyncDependenceAnalysisT::DivergenceDescriptor;
341
343 std::tuple<ConstValueRefT, InstructionT *, CycleRef>;
344
347 : Context(CI.getSSAContext()), F(*Context.getFunction()), CI(CI),
348 TTI(TTI), DT(DT), SDA(Context, DT, CI) {}
349
351
352 const FunctionT &getFunction() const { return F; }
353
354 const CycleInfoT &getCycleInfo() const { return CI; }
355
356 /// \brief Mark \p UniVal as a value that is always uniform.
357 void addUniformOverride(const InstructionT &Instr);
358
359 /// \brief Examine \p I for divergent outputs and add to the worklist.
360 void markDivergent(const InstructionT &I);
361
362 /// \brief Mark \p DivVal as a divergent value by removing it from
363 /// UniformValues. \returns Whether the tracked divergence state of
364 /// \p DivVal changed.
365 bool markDivergent(ConstValueRefT DivVal);
366
367 /// \brief Mark outputs of \p Instr as divergent.
368 /// \returns Whether the tracked divergence state of any output has changed.
369 bool markDefsDivergent(const InstructionT &Instr);
370
371 /// \brief Propagate divergence to all instructions in the region.
372 /// Divergence is seeded by calls to \p markDivergent.
373 void compute();
374
375 /// \brief Whether \p Val will always return a uniform value regardless of its
376 /// operands
377 bool isAlwaysUniform(const InstructionT &Instr) const;
378
379 bool hasDivergentDefs(const InstructionT &I) const;
380
382 assert(I.isTerminator() && "Expected a terminator instruction!");
383 return DivergentTermBlocks.contains(I.getParent());
384 };
385
386 /// \brief Whether \p Val is divergent at its definition.
387 /// When the target has no branch divergence, compute() is never called
388 /// and everything is uniform. Otherwise, values not in UniformValues
389 /// (e.g. newly created) are conservatively treated as divergent.
392 return false;
393 // Only values that were present during analysis are tracked in
394 // UniformValues (Instructions/Arguments for IR, Registers for MIR).
395 // Other values (e.g. constants, globals) are always uniform but are
396 // not added to UniformValues; this check avoids false divergence.
397 if (ContextT::isAlwaysUniform(V))
398 return false;
399 return !UniformValues.contains(V);
400 }
401
402 bool isDivergentUse(const UseT &U) const;
403
404 bool hasDivergentTerminator(const BlockT &B) const {
405 return DivergentTermBlocks.contains(&B);
406 }
407
408 /// Call before erasing \p V, or a later instruction reusing its address
409 /// may be misclassified as uniform.
411
412 void print(raw_ostream &Out) const;
413
414 /// Print divergent arguments and return true if any were found.
415 /// IR specialization iterates F.args(); default is a no-op.
416 bool printDivergentArgs(raw_ostream &Out) const;
417
419
421
422 /// Check if an instruction with Custom uniformity can be proven uniform
423 /// based on its operands. This queries the target-specific callback.
424 bool isCustomUniform(const InstructionT &I) const;
425
426 /// \brief Add an instruction that requires custom uniformity analysis.
428
429protected:
430 const ContextT &Context;
431 const FunctionT &F;
433 const TargetTransformInfo *TTI = nullptr;
434
435 // Whether the target has branch divergence. Set at the start of compute(),
436 // which is only called when the target has branch divergence. When false,
437 // isDivergent() returns false for all values.
439
441
442 // Values known to be uniform. Populated in initialize() with all values,
443 // then values are removed as divergence is propagated. After analysis,
444 // values not in this set are conservatively treated as divergent.
446
447 // Internal worklist for divergence propagation.
448 std::vector<const InstructionT *> Worklist;
449
450 // Set of instructions that require custom uniformity analysis based on
451 // operand uniformity.
453
454 /// \brief Mark \p Term as divergent and push all Instructions that become
455 /// divergent as a result on the worklist.
456 void analyzeControlDivergence(const InstructionT &Term);
457
458private:
459 const DominatorTreeT &DT;
460
461 // Recognized cycles with divergent exits.
462 SmallSetVector<CycleRef, 8> DivergentExitCycles;
463
464 // Cycles assumed to be divergent.
465 //
466 // We don't use a set here because every insertion needs an explicit
467 // traversal of all existing members.
468 SmallVector<CycleRef> AssumedDivergent;
469
470 // The SDA links divergent branches to divergent control-flow joins.
472
473 // Set of known-uniform values.
475
476 /// \brief Mark all nodes in \p JoinBlock as divergent and push them on
477 /// the worklist.
478 void taintAndPushAllDefs(const BlockT &JoinBlock);
479
480 /// \brief Mark all phi nodes in \p JoinBlock as divergent and push them on
481 /// the worklist.
482 void taintAndPushPhiNodes(const BlockT &JoinBlock);
483
484 /// \brief Identify all Instructions that become divergent because \p DivExit
485 /// is a divergent cycle exit of \p DivCycle. Mark those instructions as
486 /// divergent and push them on the worklist.
487 void propagateCycleExitDivergence(const BlockT &DivExit, CycleRef DivCycle);
488
489 /// Mark as divergent all external uses of values defined in \p DefCycle.
490 void analyzeCycleExitDivergence(CycleRef DefCycle);
491
492 /// \brief Mark as divergent all uses of \p I that are outside \p DefCycle.
493 void propagateTemporalDivergence(const InstructionT &I, CycleRef DefCycle);
494
495 /// \brief Push all users of \p Val (in the region) to the worklist.
496 void pushUsers(const InstructionT &I);
497 void pushUsers(ConstValueRefT V);
498
499 bool usesValueFromCycle(const InstructionT &I, CycleRef DefCycle) const;
500
501 /// \brief Whether \p Def is divergent when read in \p ObservingBlock.
502 bool isTemporalDivergent(const BlockT &ObservingBlock,
503 const InstructionT &Def) const;
504};
505
506template <typename ImplT>
508 delete Impl;
509}
510
511/// Compute divergence starting with a divergent branch.
512template <typename ContextT> class DivergencePropagator {
513public:
514 using BlockT = typename ContextT::BlockT;
515 using DominatorTreeT = typename ContextT::DominatorTreeT;
516 using FunctionT = typename ContextT::FunctionT;
517 using ValueRefT = typename ContextT::ValueRefT;
518
520
524 typename SyncDependenceAnalysisT::DivergenceDescriptor;
525
530 const ContextT &Context;
531
532 // Track blocks that receive a new label. Every time we relabel a
533 // cycle header, we make another pass over the modified post-order in
534 // order to propagate the header label. The bit vector also allows
535 // us to skip labels that have not changed.
537
538 // divergent join and cycle exit descriptor.
539 std::unique_ptr<DivergenceDescriptorT> DivDesc;
540
541 // Dominating definition at each block on diverged paths, indexed by block
542 // number. Transient to this propagation; not stored in the descriptor.
543 // * label(B) == C : C is the dominating definition at B
544 // * label(B) == nullptr : we haven't seen B yet
545 // * label(B) == B : B is a join of disjoint paths, an immediate
546 // successor of the divergent term, or the term
547 // itself
549
550 const BlockT *&label(const BlockT *BB) {
552 }
553
555 const CycleInfoT &CI, const BlockT &DivTermBlock)
557 Context(CI.getSSAContext()), DivDesc(new DivergenceDescriptorT),
559 GraphTraits<const FunctionT *>::getMaxNumber(CI.getFunction()),
560 nullptr) {}
561
563 Out << "Propagator::BlockLabels {\n";
564 for (int BlockIdx = (int)CyclePOT.size() - 1; BlockIdx >= 0; --BlockIdx) {
565 const auto *Block = CyclePOT[BlockIdx];
566 const auto *Label = label(Block);
567 Out << Context.print(Block) << "(" << BlockIdx << ") : ";
568 if (!Label) {
569 Out << "<null>\n";
570 } else {
571 Out << Context.print(Label) << "\n";
572 }
573 }
574 Out << "}\n";
575 }
576
577 // Push a definition (\p PushedLabel) to \p SuccBlock and return whether this
578 // causes a divergent join.
579 bool computeJoin(const BlockT &SuccBlock, const BlockT &PushedLabel) {
580 const auto *OldLabel = label(&SuccBlock);
581
582 LLVM_DEBUG(dbgs() << "labeling " << Context.print(&SuccBlock) << ":\n"
583 << "\tpushed label: " << Context.print(&PushedLabel)
584 << "\n"
585 << "\told label: " << Context.print(OldLabel) << "\n");
586
587 // Early exit if there is no change in the label.
588 if (OldLabel == &PushedLabel)
589 return false;
590
591 if (OldLabel != &SuccBlock) {
592 auto SuccIdx = CyclePOT.getIndex(&SuccBlock);
593 // Assigning a new label, mark this in FreshLabels.
594 LLVM_DEBUG(dbgs() << "\tfresh label: " << SuccIdx << "\n");
595 FreshLabels.set(SuccIdx);
596 }
597
598 // This is not a join if the succ was previously unlabeled.
599 if (!OldLabel) {
600 LLVM_DEBUG(dbgs() << "\tnew label: " << Context.print(&PushedLabel)
601 << "\n");
602 label(&SuccBlock) = &PushedLabel;
603 return false;
604 }
605
606 // This is a new join. Label the join block as itself, and not as
607 // the pushed label.
608 LLVM_DEBUG(dbgs() << "\tnew label: " << Context.print(&SuccBlock) << "\n");
609 label(&SuccBlock) = &SuccBlock;
610
611 return true;
612 }
613
614 // visiting a virtual cycle exit edge from the cycle header --> temporal
615 // divergence on join
616 bool visitCycleExitEdge(const BlockT &ExitBlock, const BlockT &Label) {
617 if (!computeJoin(ExitBlock, Label))
618 return false;
619
620 // Identified a divergent cycle exit
621 DivDesc->CycleDivBlocks.insert(&ExitBlock);
622 LLVM_DEBUG(dbgs() << "\tDivergent cycle exit: " << Context.print(&ExitBlock)
623 << "\n");
624 return true;
625 }
626
627 // process \p SuccBlock with reaching definition \p Label
628 bool visitEdge(const BlockT &SuccBlock, const BlockT &Label) {
629 if (!computeJoin(SuccBlock, Label))
630 return false;
631
632 // Divergent, disjoint paths join.
633 DivDesc->JoinDivBlocks.insert(&SuccBlock);
634 LLVM_DEBUG(dbgs() << "\tDivergent join: " << Context.print(&SuccBlock)
635 << "\n");
636 return true;
637 }
638
639 std::unique_ptr<DivergenceDescriptorT> computeJoinPoints() {
641
642 LLVM_DEBUG(dbgs() << "SDA:computeJoinPoints: "
643 << Context.print(&DivTermBlock) << "\n");
644
645 int DivTermIdx = CyclePOT.getIndex(&DivTermBlock);
646 CycleRef DivTermCycle = CI.getCycle(&DivTermBlock);
647
648 // Locate the largest ancestor cycle that is not reducible and does not
649 // contain a reducible ancestor. This is done with a lambda that is defined
650 // and invoked in the same statement.
651 CycleRef IrreducibleAncestor = [this](CycleRef C) -> CycleRef {
652 if (!C)
653 return CycleRef();
654 if (CI.isReducible(C))
655 return CycleRef();
656 while (CycleRef P = CI.getParentCycle(C)) {
657 if (CI.isReducible(P))
658 return C;
659 C = P;
660 }
661 assert(!CI.getParentCycle(C));
662 assert(!CI.isReducible(C));
663 return C;
664 }(DivTermCycle);
665
666 // Bootstrap with branch targets
667 for (const auto *SuccBlock : successors(&DivTermBlock)) {
668 if (DivTermCycle && !CI.contains(DivTermCycle, SuccBlock)) {
669 // If DivTerm exits the cycle immediately, computeJoin() might
670 // not reach SuccBlock with a different label. We need to
671 // check for this exit now.
672 DivDesc->CycleDivBlocks.insert(SuccBlock);
673 LLVM_DEBUG(dbgs() << "\tImmediate divergent cycle exit: "
674 << Context.print(SuccBlock) << "\n");
675 }
676 visitEdge(*SuccBlock, *SuccBlock);
677 }
678
679 // Technically propagation can continue until it reaches the last node.
680 //
681 // For efficiency, propagation can stop if FreshLabels.count()==1. But
682 // For irreducible cycles, let propagation continue until it reaches
683 // out of irreducible cycles (see code for details.)
684 while (true) {
685 auto BlockIdx = FreshLabels.find_last();
686 if (BlockIdx == -1)
687 break;
688
689 const auto *Block = CyclePOT[BlockIdx];
690 // If no irreducible cycle, stop if freshLabels.count() == 1 and Block
691 // is the IPD. If it is in any irreducible cycle, continue propagation.
692 if (FreshLabels.count() == 1 &&
693 (!IrreducibleAncestor || !CI.contains(IrreducibleAncestor, Block)))
694 break;
695
696 LLVM_DEBUG(dbgs() << "Current labels:\n"; printDefs(dbgs()));
697
698 FreshLabels.reset(BlockIdx);
699 if (BlockIdx == DivTermIdx) {
700 LLVM_DEBUG(dbgs() << "Skipping DivTermBlock\n");
701 continue;
702 }
703
704 LLVM_DEBUG(dbgs() << "visiting " << Context.print(Block) << " at index "
705 << BlockIdx << "\n");
706
707 const auto *Label = label(Block);
708 assert(Label);
709
710 // If the current block is the header of a reducible cycle, then the label
711 // should be propagated to the cycle exits. If this cycle contains the
712 // branch, then those exits are divergent exits. This is true for any DFS.
713 //
714 // If some DFS has a reducible cycle C with header H, then for
715 // any other DFS, H is the header of a cycle C' that is a
716 // superset of C.
717 //
718 // - For a divergent branch inside the subgraph C, any join node inside
719 // C is either H, or some node encountered by paths within C, without
720 // passing through H.
721 //
722 // - For a divergent branch outside the subgraph C, H is the only node
723 // in C reachable from multiple paths since it is the only entry to C.
724 LLVM_DEBUG(dbgs() << "Check for reducible cycle: " << Context.print(Block)
725 << '\n');
726 if (CyclePOT.isReducibleCycleHeader(Block)) {
727 CycleRef BlockCycle = CI.getCycle(Block);
728 LLVM_DEBUG(dbgs() << CI.print(BlockCycle) << '\n');
729 SmallVector<BlockT *, 4> BlockCycleExits;
730 CI.getExitBlocks(BlockCycle, BlockCycleExits);
731 bool BranchIsInside = CI.contains(BlockCycle, &DivTermBlock);
732 for (auto *BlockCycleExit : BlockCycleExits) {
733 if (BranchIsInside)
734 visitCycleExitEdge(*BlockCycleExit, *Label);
735 // Propagate the label to the exit block.
736 visitEdge(*BlockCycleExit, *Label);
737 }
738 } else {
739 for (const auto *SuccBlock : successors(Block))
740 visitEdge(*SuccBlock, *Label);
741 }
742 }
743
744 LLVM_DEBUG(dbgs() << "Final labeling:\n"; printDefs(dbgs()));
745
746 // Check every cycle containing DivTermBlock for exit divergence.
747 // A cycle has exit divergence if the label of an exit block does
748 // not match the label of its header.
749 for (auto C = CI.getCycle(&DivTermBlock); C; C = CI.getParentCycle(C)) {
750 if (CI.isReducible(C)) {
751 // The exit divergence of a reducible cycle is recorded while
752 // propagating labels.
753 continue;
754 }
756 CI.getExitBlocks(C, Exits);
757 auto *Header = CI.getHeader(C);
758 auto *HeaderLabel = label(Header);
759 for (const auto *Exit : Exits) {
760 if (label(Exit) != HeaderLabel) {
761 // Identified a divergent cycle exit
762 DivDesc->CycleDivBlocks.insert(Exit);
763 LLVM_DEBUG(dbgs() << "\tDivergent cycle exit: " << Context.print(Exit)
764 << "\n");
765 }
766 }
767 }
768
769 return std::move(DivDesc);
770 }
771};
772
773template <typename ContextT>
775 const ContextT &Context, const DominatorTreeT &DT, const CycleInfoT &CI)
776 : CyclePO(Context), DT(DT), CI(CI) {
777 CyclePO.compute(CI);
778}
779
780template <typename ContextT>
782 const BlockT *DivTermBlock) -> const DivergenceDescriptor & {
783 // trivial case
784 if (succ_size(DivTermBlock) <= 1) {
785 return EmptyDivergenceDesc;
786 }
787
788 // already available in cache?
789 auto ItCached = CachedControlDivDescs.find(DivTermBlock);
790 if (ItCached != CachedControlDivDescs.end())
791 return *ItCached->second;
792
793 // compute all join points
794 DivergencePropagatorT Propagator(CyclePO, DT, CI, *DivTermBlock);
795 auto DivDesc = Propagator.computeJoinPoints();
796
797 auto PrintBlockSet = [&](ConstBlockSet &Blocks) {
798 return Printable([&](raw_ostream &Out) {
799 Out << "[";
800 ListSeparator LS;
801 for (const auto *BB : Blocks) {
802 Out << LS << CI.getSSAContext().print(BB);
803 }
804 Out << "]\n";
805 });
806 };
807
809 dbgs() << "\nResult (" << CI.getSSAContext().print(DivTermBlock)
810 << "):\n JoinDivBlocks: " << PrintBlockSet(DivDesc->JoinDivBlocks)
811 << " CycleDivBlocks: " << PrintBlockSet(DivDesc->CycleDivBlocks)
812 << "\n");
813 (void)PrintBlockSet;
814
815 auto ItInserted =
816 CachedControlDivDescs.try_emplace(DivTermBlock, std::move(DivDesc));
817 assert(ItInserted.second);
818 return *ItInserted.first->second;
819}
820
821template <typename ContextT>
823 const InstructionT &I) {
824 if (isAlwaysUniform(I))
825 return;
826 // For custom uniformity candidates, check if the instruction can be
827 // proven uniform based on which operands are uniform/divergent.
828 // The candidate will be re-evaluated as operands become divergent.
829 if (CustomUniformityCandidates.contains(&I)) {
830 if (isCustomUniform(I))
831 return;
832 }
833 bool Marked = false;
834 if (I.isTerminator()) {
835 Marked = DivergentTermBlocks.insert(I.getParent()).second;
836 if (Marked) {
837 LLVM_DEBUG(dbgs() << "marked divergent term block: "
838 << Context.print(I.getParent()) << "\n");
839 }
840 } else {
841 Marked = markDefsDivergent(I);
842 }
843
844 if (Marked)
845 Worklist.push_back(&I);
846}
847
848template <typename ContextT>
850 ConstValueRefT Val) {
851 if (UniformValues.erase(Val)) {
852 LLVM_DEBUG(dbgs() << "marked divergent: " << Context.print(Val) << "\n");
853 return true;
854 }
855 return false;
856}
857
858template <typename ContextT>
860 const InstructionT &Instr) {
861 UniformOverrides.insert(&Instr);
862}
863
864template <typename ContextT>
869
870// Mark as divergent all external uses of values defined in \p DefCycle.
871//
872// A value V defined by a block B inside \p DefCycle may be used outside the
873// cycle only if the use is a PHI in some exit block, or B dominates some exit
874// block. Thus, we check uses as follows:
875//
876// - Check all PHIs in all exit blocks for inputs defined inside \p DefCycle.
877// - For every block B inside \p DefCycle that dominates at least one exit
878// block, check all uses outside \p DefCycle.
879//
880// FIXME: This function does not distinguish between divergent and uniform
881// exits. For each divergent exit, only the values that are live at that exit
882// need to be propagated as divergent at their use outside the cycle.
883template <typename ContextT>
884void GenericUniformityAnalysisImpl<ContextT>::analyzeCycleExitDivergence(
885 CycleRef DefCycle) {
887 CI.getExitBlocks(DefCycle, Exits);
888 for (auto *Exit : Exits) {
889 for (auto &Phi : Exit->phis()) {
890 if (usesValueFromCycle(Phi, DefCycle)) {
891 markDivergent(Phi);
892 }
893 }
894 }
895
896 for (auto *BB : CI.getBlocks(DefCycle)) {
897 if (!llvm::any_of(Exits,
898 [&](BlockT *Exit) { return DT.dominates(BB, Exit); }))
899 continue;
900 for (auto &II : *BB) {
901 propagateTemporalDivergence(II, DefCycle);
902 }
903 }
904}
905
906template <typename ContextT>
907void GenericUniformityAnalysisImpl<ContextT>::propagateCycleExitDivergence(
908 const BlockT &DivExit, CycleRef InnerDivCycle) {
909 LLVM_DEBUG(dbgs() << "\tpropCycleExitDiv " << Context.print(&DivExit)
910 << "\n");
911 CycleRef DivCycle = InnerDivCycle;
912 CycleRef OuterDivCycle = DivCycle;
913 CycleRef ExitLevelCycle = CI.getCycle(&DivExit);
914 const unsigned CycleExitDepth =
915 ExitLevelCycle ? CI.getDepth(ExitLevelCycle) : 0;
916
917 // Find outer-most cycle that does not contain \p DivExit
918 while (DivCycle && CI.getDepth(DivCycle) > CycleExitDepth) {
919 LLVM_DEBUG(dbgs() << " Found exiting cycle: "
920 << Context.print(CI.getHeader(DivCycle)) << "\n");
921 OuterDivCycle = DivCycle;
922 DivCycle = CI.getParentCycle(DivCycle);
923 }
924 LLVM_DEBUG(dbgs() << "\tOuter-most exiting cycle: "
925 << Context.print(CI.getHeader(OuterDivCycle)) << "\n");
926
927 if (!DivergentExitCycles.insert(OuterDivCycle))
928 return;
929
930 // Exit divergence does not matter if the cycle itself is assumed to
931 // be divergent.
932 for (auto C : AssumedDivergent) {
933 if (CI.contains(C, OuterDivCycle))
934 return;
935 }
936
937 analyzeCycleExitDivergence(OuterDivCycle);
938}
939
940template <typename ContextT>
941void GenericUniformityAnalysisImpl<ContextT>::taintAndPushAllDefs(
942 const BlockT &BB) {
943 LLVM_DEBUG(dbgs() << "taintAndPushAllDefs " << Context.print(&BB) << "\n");
944 for (const auto &I : instrs(BB)) {
945 // Terminators do not produce values; they are divergent only if
946 // the condition is divergent. That is handled when the divergent
947 // condition is placed in the worklist.
948 if (I.isTerminator())
949 break;
950
951 markDivergent(I);
952 }
953}
954
955/// Mark divergent phi nodes in a join block
956template <typename ContextT>
957void GenericUniformityAnalysisImpl<ContextT>::taintAndPushPhiNodes(
958 const BlockT &JoinBlock) {
959 LLVM_DEBUG(dbgs() << "taintAndPushPhiNodes in " << Context.print(&JoinBlock)
960 << "\n");
961 for (const auto &Phi : JoinBlock.phis()) {
962 // FIXME: The non-undef value is not constant per se; it just happens to be
963 // uniform and may not dominate this PHI. So assuming that the same value
964 // reaches along all incoming edges may itself be undefined behaviour. This
965 // particular interpretation of the undef value was added to
966 // DivergenceAnalysis in the following review:
967 //
968 // https://reviews.llvm.org/D19013
969 if (ContextT::isConstantOrUndefValuePhi(Phi))
970 continue;
971 markDivergent(Phi);
972 }
973}
974
975/// Add \p Candidate to \p Cycles if it is not already contained in \p Cycles.
976///
977/// \return true iff \p Candidate was added to \p Cycles.
978template <typename CycleInfoT>
979bool insertIfNotContained(const CycleInfoT &CI, SmallVector<CycleRef> &Cycles,
980 CycleRef Candidate) {
981 if (llvm::any_of(Cycles,
982 [&](CycleRef C) { return CI.contains(C, Candidate); }))
983 return false;
984 Cycles.push_back(Candidate);
985 return true;
986}
987
988/// Return the outermost cycle made divergent by branch outside it.
989///
990/// If two paths that diverged outside an irreducible cycle join
991/// inside that cycle, then that whole cycle is assumed to be
992/// divergent. This does not apply if the cycle is reducible.
993template <typename CycleInfoT, typename BlockT>
994CycleRef getExtDivCycle(const CycleInfoT &CI, CycleRef Cycle,
995 const BlockT *DivTermBlock, const BlockT *JoinBlock) {
996 assert(Cycle);
997 assert(CI.contains(Cycle, JoinBlock));
998
999 if (CI.contains(Cycle, DivTermBlock))
1000 return CycleRef();
1001
1002 CycleRef OriginalCycle = Cycle;
1003 CycleRef Parent = CI.getParentCycle(Cycle);
1004 while (Parent && !CI.contains(Parent, DivTermBlock)) {
1005 Cycle = Parent;
1006 Parent = CI.getParentCycle(Cycle);
1007 }
1008
1009 // If the original cycle is not the outermost cycle, then the outermost cycle
1010 // is irreducible. If the outermost cycle were reducible, then external
1011 // diverged paths would not reach the original inner cycle.
1012 (void)OriginalCycle;
1013 assert(Cycle == OriginalCycle || !CI.isReducible(Cycle));
1014
1015 if (CI.isReducible(Cycle)) {
1016 assert(CI.getHeader(Cycle) == JoinBlock);
1017 return CycleRef();
1018 }
1019
1020 LLVM_DEBUG(dbgs() << "cycle made divergent by external branch\n");
1021 return Cycle;
1022}
1023
1024/// Return the outermost cycle made divergent by branch inside it.
1025///
1026/// This checks the "diverged entry" criterion defined in the
1027/// docs/ConvergenceAnalysis.html.
1028template <typename ContextT, typename CycleInfoT, typename BlockT,
1029 typename DominatorTreeT>
1030CycleRef getIntDivCycle(const CycleInfoT &CI, CycleRef Cycle,
1031 const BlockT *DivTermBlock, const BlockT *JoinBlock,
1032 const DominatorTreeT &DT, ContextT &Context) {
1033 LLVM_DEBUG(dbgs() << "examine join " << Context.print(JoinBlock)
1034 << " for internal branch " << Context.print(DivTermBlock)
1035 << "\n");
1036 if (DT.properlyDominates(DivTermBlock, JoinBlock))
1037 return CycleRef();
1038
1039 // Find the smallest common cycle, if one exists.
1040 assert(Cycle && CI.contains(Cycle, JoinBlock));
1041 while (Cycle && !CI.contains(Cycle, DivTermBlock)) {
1042 Cycle = CI.getParentCycle(Cycle);
1043 }
1044 if (!Cycle || CI.isReducible(Cycle))
1045 return CycleRef();
1046
1047 if (DT.properlyDominates(CI.getHeader(Cycle), JoinBlock))
1048 return CycleRef();
1049
1050 LLVM_DEBUG(dbgs() << " header " << Context.print(CI.getHeader(Cycle))
1051 << " does not dominate join\n");
1052
1053 CycleRef Parent = CI.getParentCycle(Cycle);
1054 while (Parent && !DT.properlyDominates(CI.getHeader(Parent), JoinBlock)) {
1055 LLVM_DEBUG(dbgs() << " header " << Context.print(CI.getHeader(Parent))
1056 << " does not dominate join\n");
1057 Cycle = Parent;
1058 Parent = CI.getParentCycle(Parent);
1059 }
1060
1061 LLVM_DEBUG(dbgs() << " cycle made divergent by internal branch\n");
1062 return Cycle;
1063}
1064
1065template <typename ContextT, typename CycleInfoT, typename BlockT,
1066 typename DominatorTreeT>
1067CycleRef
1068getOutermostDivergentCycle(const CycleInfoT &CI, CycleRef Cycle,
1069 const BlockT *DivTermBlock, const BlockT *JoinBlock,
1070 const DominatorTreeT &DT, ContextT &Context) {
1071 if (!Cycle)
1072 return CycleRef();
1073
1074 // First try to expand Cycle to the largest that contains JoinBlock
1075 // but not DivTermBlock.
1076 CycleRef Ext = getExtDivCycle(CI, Cycle, DivTermBlock, JoinBlock);
1077
1078 // Continue expanding to the largest cycle that contains both.
1079 CycleRef Int =
1080 getIntDivCycle(CI, Cycle, DivTermBlock, JoinBlock, DT, Context);
1081
1082 if (Int)
1083 return Int;
1084 return Ext;
1085}
1086
1087template <typename ContextT>
1088bool GenericUniformityAnalysisImpl<ContextT>::isTemporalDivergent(
1089 const BlockT &ObservingBlock, const InstructionT &Def) const {
1090 const BlockT *DefBlock = Def.getParent();
1091 for (auto C = CI.getCycle(DefBlock); C && !CI.contains(C, &ObservingBlock);
1092 C = CI.getParentCycle(C)) {
1093 if (DivergentExitCycles.contains(C)) {
1094 return true;
1095 }
1096 }
1097 return false;
1098}
1099
1100template <typename ContextT>
1102 const InstructionT &Term) {
1103 const auto *DivTermBlock = Term.getParent();
1104 DivergentTermBlocks.insert(DivTermBlock);
1105 LLVM_DEBUG(dbgs() << "analyzeControlDiv " << Context.print(DivTermBlock)
1106 << "\n");
1107
1108 // Don't propagate divergence from unreachable blocks.
1109 if (!DT.isReachableFromEntry(DivTermBlock))
1110 return;
1111
1112 const auto &DivDesc = SDA.getJoinBlocks(DivTermBlock);
1113 SmallVector<CycleRef> DivCycles;
1114
1115 // Iterate over all blocks now reachable by a disjoint path join
1116 for (const auto *JoinBlock : DivDesc.JoinDivBlocks) {
1117 CycleRef C = CI.getCycle(JoinBlock);
1118 LLVM_DEBUG(dbgs() << "visiting join block " << Context.print(JoinBlock)
1119 << "\n");
1120 if (CycleRef Outermost = getOutermostDivergentCycle(
1121 CI, C, DivTermBlock, JoinBlock, DT, Context)) {
1122 LLVM_DEBUG(dbgs() << "found divergent cycle\n");
1123 DivCycles.push_back(Outermost);
1124 continue;
1125 }
1126 taintAndPushPhiNodes(*JoinBlock);
1127 }
1128
1129 // Sort by order of decreasing depth. This allows later cycles to be skipped
1130 // because they are already contained in earlier ones.
1131 llvm::sort(DivCycles, [this](CycleRef A, CycleRef B) {
1132 return CI.getDepth(A) > CI.getDepth(B);
1133 });
1134
1135 // Cycles that are assumed divergent due to the diverged entry
1136 // criterion potentially contain temporal divergence depending on
1137 // the DFS chosen. Conservatively, all values produced in such a
1138 // cycle are assumed divergent. "Cycle invariant" values may be
1139 // assumed uniform, but that requires further analysis.
1140 for (auto C : DivCycles) {
1141 if (!insertIfNotContained(CI, AssumedDivergent, C))
1142 continue;
1143 LLVM_DEBUG(dbgs() << "process divergent cycle\n");
1144 for (const BlockT *BB : CI.getBlocks(C)) {
1145 taintAndPushAllDefs(*BB);
1146 }
1147 }
1148
1149 CycleRef BranchCycle = CI.getCycle(DivTermBlock);
1150 assert(DivDesc.CycleDivBlocks.empty() || BranchCycle);
1151 for (const auto *DivExitBlock : DivDesc.CycleDivBlocks) {
1152 propagateCycleExitDivergence(*DivExitBlock, BranchCycle);
1153 }
1154}
1155
1156template <typename ContextT>
1158 HasBranchDivergence = true;
1159
1160 // All values on the Worklist are divergent.
1161 // Their users may not have been updated yet.
1162 while (!Worklist.empty()) {
1163 const InstructionT *I = Worklist.back();
1164 Worklist.pop_back();
1165
1166 LLVM_DEBUG(dbgs() << "worklist pop: " << Context.print(I) << "\n");
1167
1168 if (I->isTerminator()) {
1170 continue;
1171 }
1172
1173 // propagate value divergence to users
1174 assert(hasDivergentDefs(*I) && "Worklist invariant violated!");
1175 pushUsers(*I);
1176 }
1177}
1178
1179template <typename ContextT>
1184
1185template <typename ContextT>
1187 const InstructionT &Instr) const {
1188 return UniformOverrides.contains(&Instr);
1189}
1190
1191template <typename ContextT>
1196
1197template <typename ContextT>
1199 const DominatorTreeT &DT, const CycleInfoT &CI,
1200 const TargetTransformInfo *TTI) {
1201 DA.reset(new ImplT{DT, CI, TTI});
1202}
1203
1204template <typename ContextT>
1206 // When we print Value, LLVM IR instruction, we want to print extra new line.
1207 // In LLVM IR print function for Value does not print new line at the end.
1208 // In MIR print for MachineInstr prints new line at the end.
1209 constexpr bool IsMIR = std::is_same<InstructionT, MachineInstr>::value;
1210 std::string NewLine = IsMIR ? "" : "\n";
1211
1212 bool FoundDivergence = false;
1213
1214 FoundDivergence |= printDivergentArgs(OS);
1215
1216 if (!AssumedDivergent.empty()) {
1217 FoundDivergence = true;
1218 OS << "CYCLES ASSUMED DIVERGENT:\n";
1219 for (auto C : AssumedDivergent) {
1220 OS << " " << CI.print(C) << '\n';
1221 }
1222 }
1223
1224 if (!DivergentExitCycles.empty()) {
1225 FoundDivergence = true;
1226 OS << "CYCLES WITH DIVERGENT EXIT:\n";
1227 for (auto C : DivergentExitCycles) {
1228 OS << " " << CI.print(C) << '\n';
1229 }
1230 }
1231
1232 if (!TemporalDivergenceList.empty()) {
1233 FoundDivergence = true;
1234 OS << "\nTEMPORAL DIVERGENCE LIST:\n";
1235
1236 for (auto [Val, UseInst, C] : TemporalDivergenceList) {
1237 OS << "Value :" << Context.print(Val) << NewLine
1238 << "Used by :" << Context.print(UseInst) << NewLine
1239 << "Outside cycle :" << CI.print(C) << "\n\n";
1240 }
1241 }
1242
1243 for (auto &Block : F) {
1244 OS << "\nBLOCK " << Context.print(&Block) << '\n';
1245
1246 OS << "DEFINITIONS\n";
1248 Context.appendBlockDefs(Defs, Block);
1249 for (auto Value : Defs) {
1250 if (isDivergent(Value)) {
1251 FoundDivergence = true;
1252 OS << " DIVERGENT: ";
1253 } else {
1254 OS << " ";
1255 }
1256 OS << Context.print(Value) << NewLine;
1257 }
1258
1259 OS << "TERMINATORS\n";
1261 Context.appendBlockTerms(Terms, Block);
1262 bool DivergentTerminators = hasDivergentTerminator(Block);
1263 if (DivergentTerminators)
1264 FoundDivergence = true;
1265 for (auto *T : Terms) {
1266 if (DivergentTerminators)
1267 OS << " DIVERGENT: ";
1268 else
1269 OS << " ";
1270 OS << Context.print(T) << NewLine;
1271 }
1272
1273 OS << "END BLOCK\n";
1274 }
1275
1276 if (!FoundDivergence)
1277 OS << "ALL VALUES UNIFORM\n";
1278}
1279
1280template <typename ContextT>
1284 return make_range(DA->TemporalDivergenceList.begin(),
1285 DA->TemporalDivergenceList.end());
1286}
1287
1288template <typename ContextT>
1289const typename ContextT::FunctionT &
1291 return DA->getFunction();
1292}
1293
1294template <typename ContextT>
1297 return DA->getCycleInfo();
1298}
1299
1300/// Whether \p V is divergent at its definition.
1301/// A default-constructed instance (no analysis computed) reports everything
1302/// as uniform, which is conservatively correct for non-divergent targets.
1303template <typename ContextT>
1305 return DA && DA->isDivergent(V);
1306}
1307
1308template <typename ContextT>
1310 const InstructionT *I) const {
1311 assert(I->isTerminator() && "Expected a terminator instruction!");
1312 return DA && DA->isDivergentTerminator(*I);
1313}
1314
1315template <typename ContextT>
1317 return DA && DA->isDivergentUse(U);
1318}
1319
1320template <typename ContextT>
1322 return DA && DA->hasDivergentTerminator(B);
1323}
1324
1325template <typename ContextT>
1327 if (DA)
1328 DA->forgetValue(V);
1329}
1330
1331/// \brief T helper function for printing.
1332template <typename ContextT>
1334 if (!DA) {
1335 Out << " Uniformity analysis not computed (no branch divergence).\n";
1336 return;
1337 }
1338 DA->print(Out);
1339}
1340
1341template <typename ContextT>
1342void llvm::ModifiedPostOrder<ContextT>::computeStackPO(
1343 SmallVectorImpl<const BlockT *> &Stack, const CycleInfoT &CI, CycleRef C,
1345 LLVM_DEBUG(dbgs() << "inside computeStackPO\n");
1346 while (!Stack.empty()) {
1347 auto *NextBB = Stack.back();
1348 if (Finalized.count(NextBB)) {
1349 Stack.pop_back();
1350 continue;
1351 }
1352 LLVM_DEBUG(dbgs() << " visiting " << CI.getSSAContext().print(NextBB)
1353 << "\n");
1354 CycleRef NestedCycle = CI.getCycle(NextBB);
1355 if (C != NestedCycle &&
1356 (!C || (NestedCycle && CI.contains(C, NestedCycle)))) {
1357 LLVM_DEBUG(dbgs() << " found a cycle\n");
1358 while (CI.getParentCycle(NestedCycle) != C)
1359 NestedCycle = CI.getParentCycle(NestedCycle);
1360
1361 SmallVector<BlockT *, 3> NestedExits;
1362 CI.getExitBlocks(NestedCycle, NestedExits);
1363 bool PushedNodes = false;
1364 for (auto *NestedExitBB : NestedExits) {
1365 LLVM_DEBUG(dbgs() << " examine exit: "
1366 << CI.getSSAContext().print(NestedExitBB) << "\n");
1367 if (C && !CI.contains(C, NestedExitBB))
1368 continue;
1369 if (Finalized.count(NestedExitBB))
1370 continue;
1371 PushedNodes = true;
1372 Stack.push_back(NestedExitBB);
1373 LLVM_DEBUG(dbgs() << " pushed exit: "
1374 << CI.getSSAContext().print(NestedExitBB) << "\n");
1375 }
1376 if (!PushedNodes) {
1377 // All loop exits finalized -> finish this node
1378 Stack.pop_back();
1379 computeCyclePO(CI, NestedCycle, Finalized);
1380 }
1381 continue;
1382 }
1383
1384 LLVM_DEBUG(dbgs() << " no nested cycle, going into DAG\n");
1385 // DAG-style
1386 bool PushedNodes = false;
1387 for (auto *SuccBB : successors(NextBB)) {
1388 LLVM_DEBUG(dbgs() << " examine succ: "
1389 << CI.getSSAContext().print(SuccBB) << "\n");
1390 if (C && !CI.contains(C, SuccBB))
1391 continue;
1392 if (Finalized.count(SuccBB))
1393 continue;
1394 PushedNodes = true;
1395 Stack.push_back(SuccBB);
1396 LLVM_DEBUG(dbgs() << " pushed succ: " << CI.getSSAContext().print(SuccBB)
1397 << "\n");
1398 }
1399 if (!PushedNodes) {
1400 // Never push nodes twice
1401 LLVM_DEBUG(dbgs() << " finishing node: "
1402 << CI.getSSAContext().print(NextBB) << "\n");
1403 Stack.pop_back();
1404 Finalized.insert(NextBB);
1405 appendBlock(*NextBB);
1406 }
1407 }
1408 LLVM_DEBUG(dbgs() << "exited computeStackPO\n");
1409}
1410
1411template <typename ContextT>
1412void ModifiedPostOrder<ContextT>::computeCyclePO(
1413 const CycleInfoT &CI, CycleRef C,
1415 LLVM_DEBUG(dbgs() << "inside computeCyclePO\n");
1417 auto *CycleHeader = CI.getHeader(C);
1418
1419 LLVM_DEBUG(dbgs() << " noted header: "
1420 << CI.getSSAContext().print(CycleHeader) << "\n");
1421 assert(!Finalized.count(CycleHeader));
1422 Finalized.insert(CycleHeader);
1423
1424 // Visit the header last
1425 LLVM_DEBUG(dbgs() << " finishing header: "
1426 << CI.getSSAContext().print(CycleHeader) << "\n");
1427 appendBlock(*CycleHeader, CI.isReducible(C));
1428
1429 // Initialize with immediate successors
1430 for (auto *BB : successors(CycleHeader)) {
1431 LLVM_DEBUG(dbgs() << " examine succ: " << CI.getSSAContext().print(BB)
1432 << "\n");
1433 if (!CI.contains(C, BB))
1434 continue;
1435 if (BB == CycleHeader)
1436 continue;
1437 if (!Finalized.count(BB)) {
1438 LLVM_DEBUG(dbgs() << " pushed succ: " << CI.getSSAContext().print(BB)
1439 << "\n");
1440 Stack.push_back(BB);
1441 }
1442 }
1443
1444 // Compute PO inside region
1445 computeStackPO(Stack, CI, C, Finalized);
1446
1447 LLVM_DEBUG(dbgs() << "exited computeCyclePO\n");
1448}
1449
1450/// \brief Generically compute the modified post order.
1451template <typename ContextT>
1455 auto *F = CI.getFunction();
1456 POIndex.assign(GraphTraits<const FunctionT *>::getMaxNumber(F), InvalidIndex);
1457 Stack.reserve(24); // FIXME made-up number
1458 Stack.push_back(&F->front());
1459 computeStackPO(Stack, CI, CycleRef(), Finalized);
1460}
1461
1462} // namespace llvm
1463
1464#undef DEBUG_TYPE
1465
1466#endif // LLVM_ADT_GENERICUNIFORMITYIMPL_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
aarch64 promote const
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
This file defines the DenseSet and SmallDenseSet classes.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
#define T
uint64_t IntrinsicInst * II
#define P(N)
This file contains some templates that are useful if you are working with the STL at all.
This file implements a set that has insertion order iteration characteristics.
This file defines the SmallPtrSet class.
This file defines the SparseBitVector class.
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static const unsigned InvalidIndex
static Function * getFunction(FunctionType *Ty, const Twine &Name, Module *M)
Opaque handle to a cycle within a GenericCycleInfo that wraps the cycle's preorder index.
Implements a dense probed hash-table based set.
Definition DenseSet.h:281
Compute divergence starting with a divergent branch.
GenericSyncDependenceAnalysis< ContextT > SyncDependenceAnalysisT
const BlockT *& label(const BlockT *BB)
typename ContextT::DominatorTreeT DominatorTreeT
bool computeJoin(const BlockT &SuccBlock, const BlockT &PushedLabel)
std::unique_ptr< DivergenceDescriptorT > DivDesc
void printDefs(raw_ostream &Out)
typename ContextT::FunctionT FunctionT
GenericCycleInfo< ContextT > CycleInfoT
SmallVector< const BlockT * > BlockLabels
ModifiedPostOrder< ContextT > ModifiedPO
std::unique_ptr< DivergenceDescriptorT > computeJoinPoints()
bool visitCycleExitEdge(const BlockT &ExitBlock, const BlockT &Label)
typename ContextT::ValueRefT ValueRefT
typename ContextT::BlockT BlockT
DivergencePropagator(const ModifiedPO &CyclePOT, const DominatorTreeT &DT, const CycleInfoT &CI, const BlockT &DivTermBlock)
bool visitEdge(const BlockT &SuccBlock, const BlockT &Label)
typename SyncDependenceAnalysisT::DivergenceDescriptor DivergenceDescriptorT
Cycle information for a function.
Locate join blocks for disjoint paths starting at a divergent branch.
GenericSyncDependenceAnalysis(const ContextT &Context, const DominatorTreeT &DT, const CycleInfoT &CI)
ModifiedPostOrder< ContextT > ModifiedPO
DivergencePropagator< ContextT > DivergencePropagatorT
SmallPtrSet< const BlockT *, 4 > ConstBlockSet
typename ContextT::DominatorTreeT DominatorTreeT
GenericCycleInfo< ContextT > CycleInfoT
typename ContextT::FunctionT FunctionT
typename ContextT::InstructionT InstructionT
typename ContextT::ValueRefT ValueRefT
const DivergenceDescriptor & getJoinBlocks(const BlockT *DivTermBlock)
Computes divergent join points and cycle exits caused by branch divergence in Term.
SmallVector< TemporalDivergenceTuple, 8 > TemporalDivergenceList
bool isAlwaysUniform(const InstructionT &Instr) const
Whether Val will always return a uniform value regardless of its operands.
bool isCustomUniform(const InstructionT &I) const
Check if an instruction with Custom uniformity can be proven uniform based on its operands.
typename ContextT::ValueRefT ValueRefT
void analyzeControlDivergence(const InstructionT &Term)
Mark Term as divergent and push all Instructions that become divergent as a result on the worklist.
void addCustomUniformityCandidate(const InstructionT *I)
Add an instruction that requires custom uniformity analysis.
bool isDivergent(ConstValueRefT V) const
Whether Val is divergent at its definition.
void recordTemporalDivergence(ConstValueRefT, const InstructionT *, CycleRef)
bool isDivergentUse(const UseT &U) const
bool hasDivergentDefs(const InstructionT &I) const
SmallPtrSet< const InstructionT *, 8 > CustomUniformityCandidates
typename ContextT::InstructionT InstructionT
bool printDivergentArgs(raw_ostream &Out) const
Print divergent arguments and return true if any were found.
void forgetValue(ConstValueRefT V)
Call before erasing V, or a later instruction reusing its address may be misclassified as uniform.
typename ContextT::ConstValueRefT ConstValueRefT
typename ContextT::DominatorTreeT DominatorTreeT
void compute()
Propagate divergence to all instructions in the region.
bool hasDivergentTerminator(const BlockT &B) const
GenericUniformityAnalysisImpl(const DominatorTreeT &DT, const CycleInfoT &CI, const TargetTransformInfo *TTI)
bool markDefsDivergent(const InstructionT &Instr)
Mark outputs of Instr as divergent.
typename ContextT::FunctionT FunctionT
std::vector< const InstructionT * > Worklist
void markDivergent(const InstructionT &I)
Examine I for divergent outputs and add to the worklist.
SmallPtrSet< const BlockT *, 32 > DivergentTermBlocks
bool isDivergentTerminator(const InstructionT &I) const
GenericCycleInfo< ContextT > CycleInfoT
void addUniformOverride(const InstructionT &Instr)
Mark UniVal as a value that is always uniform.
std::tuple< ConstValueRefT, InstructionT *, CycleRef > TemporalDivergenceTuple
typename SyncDependenceAnalysisT::DivergenceDescriptor DivergenceDescriptorT
GenericSyncDependenceAnalysis< ContextT > SyncDependenceAnalysisT
bool hasDivergentTerminator(const BlockT &B)
void print(raw_ostream &Out) const
T helper function for printing.
bool isDivergentAtDef(ConstValueRefT V) const
Whether V is divergent at its definition.
std::tuple< ConstValueRefT, InstructionT *, CycleRef > TemporalDivergenceTuple
typename ContextT::ConstValueRefT ConstValueRefT
typename ContextT::BlockT BlockT
bool isDivergentAtUse(const UseT &U) const
Whether U is divergent at its use.
const CycleInfoT & getCycleInfo() const
The cycle info this analysis was computed with.
typename ContextT::InstructionT InstructionT
const FunctionT & getFunction() const
The GPU kernel this analysis result is for.
bool isDivergentTerminator(const InstructionT *I) const
void forgetValue(ConstValueRefT V)
Call before erasing V, or a later instruction reusing its address may be misclassified as uniform.
GenericCycleInfo< ContextT > CycleInfoT
typename ContextT::DominatorTreeT DominatorTreeT
iterator_range< TemporalDivergenceTuple * > getTemporalDivergenceList() const
A helper class to return the specified delimiter string after the first invocation of operator String...
Construct a specially modified post-order traversal of cycles.
typename ContextT::FunctionT FunctionT
bool isReducibleCycleHeader(const BlockT *BB) const
void appendBlock(const BlockT &BB, bool IsReducibleCycleHeader=false)
const BlockT * operator[](size_t Idx) const
ModifiedPostOrder(const ContextT &C)
unsigned count(BlockT *BB) const
void compute(const CycleInfoT &CI)
Generically compute the modified post order.
GenericCycleInfo< ContextT > CycleInfoT
unsigned getIndex(const BlockT *BB) const
typename std::vector< BlockT * >::const_iterator const_iterator
typename ContextT::DominatorTreeT DominatorTreeT
typename ContextT::BlockT BlockT
Simple wrapper around std::function<void(raw_ostream&)>.
Definition Printable.h:38
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
A SetVector that performs no allocations if smaller than a certain size.
Definition SetVector.h:345
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
This pass provides access to the codegen interfaces that are needed for IR-level transformations.
LLVM Value Representation.
Definition Value.h:75
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:53
This is an optimization pass for GlobalISel generic memory operations.
bool insertIfNotContained(const CycleInfoT &CI, SmallVector< CycleRef > &Cycles, CycleRef Candidate)
Add Candidate to Cycles if it is not already contained in Cycles.
CycleRef getExtDivCycle(const CycleInfoT &CI, CycleRef Cycle, const BlockT *DivTermBlock, const BlockT *JoinBlock)
Return the outermost cycle made divergent by branch outside it.
auto successors(const MachineBasicBlock *BB)
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1746
CycleRef getOutermostDivergentCycle(const CycleInfoT &CI, CycleRef Cycle, const BlockT *DivTermBlock, const BlockT *JoinBlock, const DominatorTreeT &DT, ContextT &Context)
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
auto succ_size(const MachineBasicBlock *BB)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
TargetTransformInfo TTI
auto instrs(const MachineBasicBlock &BB)
CycleRef getIntDivCycle(const CycleInfoT &CI, CycleRef Cycle, const BlockT *DivTermBlock, const BlockT *JoinBlock, const DominatorTreeT &DT, ContextT &Context)
Return the outermost cycle made divergent by branch inside it.
Information discovered by the sync dependence analysis for each divergent branch.