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