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 /// Information discovered by the sync dependence analysis for each
283 /// divergent branch.
285 // Join points of diverged paths.
287 // Divergent cycle exits
289 };
290
292
293 GenericSyncDependenceAnalysis(const ContextT &Context,
294 const DominatorTreeT &DT, const CycleInfoT &CI);
295
296 /// \brief Computes divergent join points and cycle exits caused by branch
297 /// divergence in \p Term.
298 ///
299 /// This returns a pair of sets:
300 /// * The set of blocks which are reachable by disjoint paths from
301 /// \p Term.
302 /// * The set also contains cycle exits if there two disjoint paths:
303 /// one from \p Term to the cycle exit and another from \p Term to
304 /// the cycle header.
305 const DivergenceDescriptor &getJoinBlocks(const BlockT *DivTermBlock);
306
307private:
308 static inline DivergenceDescriptor EmptyDivergenceDesc;
309
310 ModifiedPO CyclePO;
311
312 const DominatorTreeT &DT;
313 const CycleInfoT &CI;
314
316 CachedControlDivDescs;
317};
318
319/// \brief Analysis that identifies uniform values in a data-parallel
320/// execution.
321///
322/// This analysis propagates divergence in a data-parallel context
323/// from sources of divergence to all users. It can be instantiated
324/// for an IR that provides a suitable SSAContext.
325template <typename ContextT> class GenericUniformityAnalysisImpl {
326public:
327 using BlockT = typename ContextT::BlockT;
328 using FunctionT = typename ContextT::FunctionT;
329 using ValueRefT = typename ContextT::ValueRefT;
330 using ConstValueRefT = typename ContextT::ConstValueRefT;
331 using UseT = typename ContextT::UseT;
332 using InstructionT = typename ContextT::InstructionT;
333 using DominatorTreeT = typename ContextT::DominatorTreeT;
334
336
339 typename SyncDependenceAnalysisT::DivergenceDescriptor;
340
342 std::tuple<ConstValueRefT, InstructionT *, CycleRef>;
343
346 : Context(CI.getSSAContext()), F(*Context.getFunction()), CI(CI),
347 TTI(TTI), DT(DT), SDA(Context, DT, CI) {}
348
350
351 const FunctionT &getFunction() const { return F; }
352
353 const CycleInfoT &getCycleInfo() const { return CI; }
354
355 /// \brief Mark \p UniVal as a value that is always uniform.
356 void addUniformOverride(const InstructionT &Instr);
357
358 /// \brief Examine \p I for divergent outputs and add to the worklist.
359 void markDivergent(const InstructionT &I);
360
361 /// \brief Mark \p DivVal as a divergent value by removing it from
362 /// UniformValues. \returns Whether the tracked divergence state of
363 /// \p DivVal changed.
364 bool markDivergent(ConstValueRefT DivVal);
365
366 /// \brief Mark outputs of \p Instr as divergent.
367 /// \returns Whether the tracked divergence state of any output has changed.
368 bool markDefsDivergent(const InstructionT &Instr);
369
370 /// \brief Propagate divergence to all instructions in the region.
371 /// Divergence is seeded by calls to \p markDivergent.
372 void compute();
373
374 /// \brief Whether \p Val will always return a uniform value regardless of its
375 /// operands
376 bool isAlwaysUniform(const InstructionT &Instr) const;
377
378 bool hasDivergentDefs(const InstructionT &I) const;
379
381 assert(I.isTerminator() && "Expected a terminator instruction!");
382 return DivergentTermBlocks.contains(I.getParent());
383 };
384
385 /// \brief Whether \p Val is divergent at its definition.
386 /// When the target has no branch divergence, compute() is never called
387 /// and everything is uniform. Otherwise, values not in UniformValues
388 /// (e.g. newly created) are conservatively treated as divergent.
391 return false;
392 // Only values that were present during analysis are tracked in
393 // UniformValues (Instructions/Arguments for IR, Registers for MIR).
394 // Other values (e.g. constants, globals) are always uniform but are
395 // not added to UniformValues; this check avoids false divergence.
396 if (ContextT::isAlwaysUniform(V))
397 return false;
398 return !UniformValues.contains(V);
399 }
400
401 bool isDivergentUse(const UseT &U) const;
402
403 bool hasDivergentTerminator(const BlockT &B) const {
404 return DivergentTermBlocks.contains(&B);
405 }
406
407 /// Call before erasing \p V, or a later instruction reusing its address
408 /// may be misclassified as uniform.
410
411 void print(raw_ostream &Out) const;
412
413 /// Print divergent arguments and return true if any were found.
414 /// IR specialization iterates F.args(); default is a no-op.
415 bool printDivergentArgs(raw_ostream &Out) const;
416
418
420
421 /// Check if an instruction with Custom uniformity can be proven uniform
422 /// based on its operands. This queries the target-specific callback.
423 bool isCustomUniform(const InstructionT &I) const;
424
425 /// \brief Add an instruction that requires custom uniformity analysis.
427
428protected:
429 const ContextT &Context;
430 const FunctionT &F;
432 const TargetTransformInfo *TTI = nullptr;
433
434 // Whether the target has branch divergence. Set at the start of compute(),
435 // which is only called when the target has branch divergence. When false,
436 // isDivergent() returns false for all values.
438
440
441 // Values known to be uniform. Populated in initialize() with all values,
442 // then values are removed as divergence is propagated. After analysis,
443 // values not in this set are conservatively treated as divergent.
445
446 // Internal worklist for divergence propagation.
447 std::vector<const InstructionT *> Worklist;
448
449 // Set of instructions that require custom uniformity analysis based on
450 // operand uniformity.
452
453 /// \brief Mark \p Term as divergent and push all Instructions that become
454 /// divergent as a result on the worklist.
455 void analyzeControlDivergence(const InstructionT &Term);
456
457private:
458 const DominatorTreeT &DT;
459
460 // Recognized cycles with divergent exits.
461 SmallSetVector<CycleRef, 8> DivergentExitCycles;
462
463 // Cycles assumed to be divergent.
464 //
465 // We don't use a set here because every insertion needs an explicit
466 // traversal of all existing members.
467 SmallVector<CycleRef> AssumedDivergent;
468
469 // The SDA links divergent branches to divergent control-flow joins.
471
472 // Set of known-uniform values.
474
475 /// \brief Mark all nodes in \p JoinBlock as divergent and push them on
476 /// the worklist.
477 void taintAndPushAllDefs(const BlockT &JoinBlock);
478
479 /// \brief Mark all phi nodes in \p JoinBlock as divergent and push them on
480 /// the worklist.
481 void taintAndPushPhiNodes(const BlockT &JoinBlock);
482
483 /// \brief Identify all Instructions that become divergent because \p DivExit
484 /// is a divergent cycle exit of \p DivCycle. Mark those instructions as
485 /// divergent and push them on the worklist.
486 void propagateCycleExitDivergence(const BlockT &DivExit, CycleRef DivCycle);
487
488 /// Mark as divergent all external uses of values defined in \p DefCycle.
489 void analyzeCycleExitDivergence(CycleRef DefCycle);
490
491 /// \brief Mark as divergent all uses of \p I that are outside \p DefCycle.
492 void propagateTemporalDivergence(const InstructionT &I, CycleRef DefCycle);
493
494 /// \brief Push all users of \p Val (in the region) to the worklist.
495 void pushUsers(const InstructionT &I);
496 void pushUsers(ConstValueRefT V);
497
498 bool usesValueFromCycle(const InstructionT &I, CycleRef DefCycle) const;
499
500 /// \brief Whether \p Def is divergent when read in \p ObservingBlock.
501 bool isTemporalDivergent(const BlockT &ObservingBlock,
502 const InstructionT &Def) const;
503};
504
505template <typename ImplT>
507 delete Impl;
508}
509
510/// Compute divergence starting with a divergent branch.
511template <typename ContextT> class DivergencePropagator {
512public:
513 using BlockT = typename ContextT::BlockT;
514 using DominatorTreeT = typename ContextT::DominatorTreeT;
515 using FunctionT = typename ContextT::FunctionT;
516 using ValueRefT = typename ContextT::ValueRefT;
517
519
523 typename SyncDependenceAnalysisT::DivergenceDescriptor;
524
529 const ContextT &Context;
530
531 // Track blocks that receive a new label. Every time we relabel a
532 // cycle header, we another pass over the modified post-order in
533 // order to propagate the header label. The bit vector also allows
534 // us to skip labels that have not changed.
536
537 // divergent join and cycle exit descriptor.
538 std::unique_ptr<DivergenceDescriptorT> DivDesc;
539
540 // Dominating definition at each block on diverged paths, indexed by block
541 // number. Transient to this propagation; not stored in the descriptor.
542 // * label(B) == C : C is the dominating definition at B
543 // * label(B) == nullptr : we haven't seen B yet
544 // * label(B) == B : B is a join of disjoint paths, an immediate
545 // successor of the divergent term, or the term
546 // itself
548
549 const BlockT *&label(const BlockT *BB) {
551 }
552
554 const CycleInfoT &CI, const BlockT &DivTermBlock)
556 Context(CI.getSSAContext()), DivDesc(new DivergenceDescriptorT),
558 GraphTraits<const FunctionT *>::getMaxNumber(CI.getFunction()),
559 nullptr) {}
560
562 Out << "Propagator::BlockLabels {\n";
563 for (int BlockIdx = (int)CyclePOT.size() - 1; BlockIdx >= 0; --BlockIdx) {
564 const auto *Block = CyclePOT[BlockIdx];
565 const auto *Label = label(Block);
566 Out << Context.print(Block) << "(" << BlockIdx << ") : ";
567 if (!Label) {
568 Out << "<null>\n";
569 } else {
570 Out << Context.print(Label) << "\n";
571 }
572 }
573 Out << "}\n";
574 }
575
576 // Push a definition (\p PushedLabel) to \p SuccBlock and return whether this
577 // causes a divergent join.
578 bool computeJoin(const BlockT &SuccBlock, const BlockT &PushedLabel) {
579 const auto *OldLabel = label(&SuccBlock);
580
581 LLVM_DEBUG(dbgs() << "labeling " << Context.print(&SuccBlock) << ":\n"
582 << "\tpushed label: " << Context.print(&PushedLabel)
583 << "\n"
584 << "\told label: " << Context.print(OldLabel) << "\n");
585
586 // Early exit if there is no change in the label.
587 if (OldLabel == &PushedLabel)
588 return false;
589
590 if (OldLabel != &SuccBlock) {
591 auto SuccIdx = CyclePOT.getIndex(&SuccBlock);
592 // Assigning a new label, mark this in FreshLabels.
593 LLVM_DEBUG(dbgs() << "\tfresh label: " << SuccIdx << "\n");
594 FreshLabels.set(SuccIdx);
595 }
596
597 // This is not a join if the succ was previously unlabeled.
598 if (!OldLabel) {
599 LLVM_DEBUG(dbgs() << "\tnew label: " << Context.print(&PushedLabel)
600 << "\n");
601 label(&SuccBlock) = &PushedLabel;
602 return false;
603 }
604
605 // This is a new join. Label the join block as itself, and not as
606 // the pushed label.
607 LLVM_DEBUG(dbgs() << "\tnew label: " << Context.print(&SuccBlock) << "\n");
608 label(&SuccBlock) = &SuccBlock;
609
610 return true;
611 }
612
613 // visiting a virtual cycle exit edge from the cycle header --> temporal
614 // divergence on join
615 bool visitCycleExitEdge(const BlockT &ExitBlock, const BlockT &Label) {
616 if (!computeJoin(ExitBlock, Label))
617 return false;
618
619 // Identified a divergent cycle exit
620 DivDesc->CycleDivBlocks.insert(&ExitBlock);
621 LLVM_DEBUG(dbgs() << "\tDivergent cycle exit: " << Context.print(&ExitBlock)
622 << "\n");
623 return true;
624 }
625
626 // process \p SuccBlock with reaching definition \p Label
627 bool visitEdge(const BlockT &SuccBlock, const BlockT &Label) {
628 if (!computeJoin(SuccBlock, Label))
629 return false;
630
631 // Divergent, disjoint paths join.
632 DivDesc->JoinDivBlocks.insert(&SuccBlock);
633 LLVM_DEBUG(dbgs() << "\tDivergent join: " << Context.print(&SuccBlock)
634 << "\n");
635 return true;
636 }
637
638 std::unique_ptr<DivergenceDescriptorT> computeJoinPoints() {
640
641 LLVM_DEBUG(dbgs() << "SDA:computeJoinPoints: "
642 << Context.print(&DivTermBlock) << "\n");
643
644 int DivTermIdx = CyclePOT.getIndex(&DivTermBlock);
645 CycleRef DivTermCycle = CI.getCycle(&DivTermBlock);
646
647 // Locate the largest ancestor cycle that is not reducible and does not
648 // contain a reducible ancestor. This is done with a lambda that is defined
649 // and invoked in the same statement.
650 CycleRef IrreducibleAncestor = [this](CycleRef C) -> CycleRef {
651 if (!C)
652 return CycleRef();
653 if (CI.isReducible(C))
654 return CycleRef();
655 while (CycleRef P = CI.getParentCycle(C)) {
656 if (CI.isReducible(P))
657 return C;
658 C = P;
659 }
660 assert(!CI.getParentCycle(C));
661 assert(!CI.isReducible(C));
662 return C;
663 }(DivTermCycle);
664
665 // Bootstrap with branch targets
666 for (const auto *SuccBlock : successors(&DivTermBlock)) {
667 if (DivTermCycle && !CI.contains(DivTermCycle, SuccBlock)) {
668 // If DivTerm exits the cycle immediately, computeJoin() might
669 // not reach SuccBlock with a different label. We need to
670 // check for this exit now.
671 DivDesc->CycleDivBlocks.insert(SuccBlock);
672 LLVM_DEBUG(dbgs() << "\tImmediate divergent cycle exit: "
673 << Context.print(SuccBlock) << "\n");
674 }
675 visitEdge(*SuccBlock, *SuccBlock);
676 }
677
678 // Technically propagation can continue until it reaches the last node.
679 //
680 // For efficiency, propagation can stop if FreshLabels.count()==1. But
681 // For irreducible cycles, let propagation continue until it reaches
682 // out of irreducible cycles (see code for details.)
683 while (true) {
684 auto BlockIdx = FreshLabels.find_last();
685 if (BlockIdx == -1)
686 break;
687
688 const auto *Block = CyclePOT[BlockIdx];
689 // If no irreducible cycle, stop if freshLable.count() = 1 and Block
690 // is the IPD. If it is in any irreducible cycle, continue propagation.
691 if (FreshLabels.count() == 1 &&
692 (!IrreducibleAncestor || !CI.contains(IrreducibleAncestor, Block)))
693 break;
694
695 LLVM_DEBUG(dbgs() << "Current labels:\n"; printDefs(dbgs()));
696
697 FreshLabels.reset(BlockIdx);
698 if (BlockIdx == DivTermIdx) {
699 LLVM_DEBUG(dbgs() << "Skipping DivTermBlock\n");
700 continue;
701 }
702
703 LLVM_DEBUG(dbgs() << "visiting " << Context.print(Block) << " at index "
704 << BlockIdx << "\n");
705
706 const auto *Label = label(Block);
707 assert(Label);
708
709 // If the current block is the header of a reducible cycle, then the label
710 // should be propagated to the cycle exits. If this cycle contains the
711 // branch, then those exits are divergent exits. This is true for any DFS.
712 //
713 // If some DFS has a reducible cycle C with header H, then for
714 // any other DFS, H is the header of a cycle C' that is a
715 // superset of C.
716 //
717 // - For a divergent branch inside the subgraph C, any join node inside
718 // C is either H, or some node encountered by paths within C, without
719 // passing through H.
720 //
721 // - For a divergent branch outside the subgraph C, H is the only node
722 // in C reachable from multiple paths since it is the only entry to C.
723 LLVM_DEBUG(dbgs() << "Check for reducible cycle: " << Context.print(Block)
724 << '\n');
725 if (CyclePOT.isReducibleCycleHeader(Block)) {
726 CycleRef BlockCycle = CI.getCycle(Block);
727 LLVM_DEBUG(dbgs() << CI.print(BlockCycle) << '\n');
728 SmallVector<BlockT *, 4> BlockCycleExits;
729 CI.getExitBlocks(BlockCycle, BlockCycleExits);
730 bool BranchIsInside = CI.contains(BlockCycle, &DivTermBlock);
731 for (auto *BlockCycleExit : BlockCycleExits) {
732 if (BranchIsInside)
733 visitCycleExitEdge(*BlockCycleExit, *Label);
734 else
735 visitEdge(*BlockCycleExit, *Label);
736 }
737 } else {
738 for (const auto *SuccBlock : successors(Block))
739 visitEdge(*SuccBlock, *Label);
740 }
741 }
742
743 LLVM_DEBUG(dbgs() << "Final labeling:\n"; printDefs(dbgs()));
744
745 // Check every cycle containing DivTermBlock for exit divergence.
746 // A cycle has exit divergence if the label of an exit block does
747 // not match the label of its header.
748 for (auto C = CI.getCycle(&DivTermBlock); C; C = CI.getParentCycle(C)) {
749 if (CI.isReducible(C)) {
750 // The exit divergence of a reducible cycle is recorded while
751 // propagating labels.
752 continue;
753 }
755 CI.getExitBlocks(C, Exits);
756 auto *Header = CI.getHeader(C);
757 auto *HeaderLabel = label(Header);
758 for (const auto *Exit : Exits) {
759 if (label(Exit) != HeaderLabel) {
760 // Identified a divergent cycle exit
761 DivDesc->CycleDivBlocks.insert(Exit);
762 LLVM_DEBUG(dbgs() << "\tDivergent cycle exit: " << Context.print(Exit)
763 << "\n");
764 }
765 }
766 }
767
768 return std::move(DivDesc);
769 }
770};
771
772template <typename ContextT>
774 const ContextT &Context, const DominatorTreeT &DT, const CycleInfoT &CI)
775 : CyclePO(Context), DT(DT), CI(CI) {
776 CyclePO.compute(CI);
777}
778
779template <typename ContextT>
781 const BlockT *DivTermBlock) -> const DivergenceDescriptor & {
782 // trivial case
783 if (succ_size(DivTermBlock) <= 1) {
784 return EmptyDivergenceDesc;
785 }
786
787 // already available in cache?
788 auto ItCached = CachedControlDivDescs.find(DivTermBlock);
789 if (ItCached != CachedControlDivDescs.end())
790 return *ItCached->second;
791
792 // compute all join points
793 DivergencePropagatorT Propagator(CyclePO, DT, CI, *DivTermBlock);
794 auto DivDesc = Propagator.computeJoinPoints();
795
796 auto PrintBlockSet = [&](ConstBlockSet &Blocks) {
797 return Printable([&](raw_ostream &Out) {
798 Out << "[";
799 ListSeparator LS;
800 for (const auto *BB : Blocks) {
801 Out << LS << CI.getSSAContext().print(BB);
802 }
803 Out << "]\n";
804 });
805 };
806
808 dbgs() << "\nResult (" << CI.getSSAContext().print(DivTermBlock)
809 << "):\n JoinDivBlocks: " << PrintBlockSet(DivDesc->JoinDivBlocks)
810 << " CycleDivBlocks: " << PrintBlockSet(DivDesc->CycleDivBlocks)
811 << "\n");
812 (void)PrintBlockSet;
813
814 auto ItInserted =
815 CachedControlDivDescs.try_emplace(DivTermBlock, std::move(DivDesc));
816 assert(ItInserted.second);
817 return *ItInserted.first->second;
818}
819
820template <typename ContextT>
822 const InstructionT &I) {
823 if (isAlwaysUniform(I))
824 return;
825 // For custom uniformity candidates, check if the instruction can be
826 // proven uniform based on which operands are uniform/divergent.
827 // The candidate will be re-evaluated as operands become divergent.
828 if (CustomUniformityCandidates.contains(&I)) {
829 if (isCustomUniform(I))
830 return;
831 }
832 bool Marked = false;
833 if (I.isTerminator()) {
834 Marked = DivergentTermBlocks.insert(I.getParent()).second;
835 if (Marked) {
836 LLVM_DEBUG(dbgs() << "marked divergent term block: "
837 << Context.print(I.getParent()) << "\n");
838 }
839 } else {
840 Marked = markDefsDivergent(I);
841 }
842
843 if (Marked)
844 Worklist.push_back(&I);
845}
846
847template <typename ContextT>
849 ConstValueRefT Val) {
850 if (UniformValues.erase(Val)) {
851 LLVM_DEBUG(dbgs() << "marked divergent: " << Context.print(Val) << "\n");
852 return true;
853 }
854 return false;
855}
856
857template <typename ContextT>
859 const InstructionT &Instr) {
860 UniformOverrides.insert(&Instr);
861}
862
863template <typename ContextT>
868
869// Mark as divergent all external uses of values defined in \p DefCycle.
870//
871// A value V defined by a block B inside \p DefCycle may be used outside the
872// cycle only if the use is a PHI in some exit block, or B dominates some exit
873// block. Thus, we check uses as follows:
874//
875// - Check all PHIs in all exit blocks for inputs defined inside \p DefCycle.
876// - For every block B inside \p DefCycle that dominates at least one exit
877// block, check all uses outside \p DefCycle.
878//
879// FIXME: This function does not distinguish between divergent and uniform
880// exits. For each divergent exit, only the values that are live at that exit
881// need to be propagated as divergent at their use outside the cycle.
882template <typename ContextT>
883void GenericUniformityAnalysisImpl<ContextT>::analyzeCycleExitDivergence(
884 CycleRef DefCycle) {
886 CI.getExitBlocks(DefCycle, Exits);
887 for (auto *Exit : Exits) {
888 for (auto &Phi : Exit->phis()) {
889 if (usesValueFromCycle(Phi, DefCycle)) {
890 markDivergent(Phi);
891 }
892 }
893 }
894
895 for (auto *BB : CI.getBlocks(DefCycle)) {
896 if (!llvm::any_of(Exits,
897 [&](BlockT *Exit) { return DT.dominates(BB, Exit); }))
898 continue;
899 for (auto &II : *BB) {
900 propagateTemporalDivergence(II, DefCycle);
901 }
902 }
903}
904
905template <typename ContextT>
906void GenericUniformityAnalysisImpl<ContextT>::propagateCycleExitDivergence(
907 const BlockT &DivExit, CycleRef InnerDivCycle) {
908 LLVM_DEBUG(dbgs() << "\tpropCycleExitDiv " << Context.print(&DivExit)
909 << "\n");
910 CycleRef DivCycle = InnerDivCycle;
911 CycleRef OuterDivCycle = DivCycle;
912 CycleRef ExitLevelCycle = CI.getCycle(&DivExit);
913 const unsigned CycleExitDepth =
914 ExitLevelCycle ? CI.getDepth(ExitLevelCycle) : 0;
915
916 // Find outer-most cycle that does not contain \p DivExit
917 while (DivCycle && CI.getDepth(DivCycle) > CycleExitDepth) {
918 LLVM_DEBUG(dbgs() << " Found exiting cycle: "
919 << Context.print(CI.getHeader(DivCycle)) << "\n");
920 OuterDivCycle = DivCycle;
921 DivCycle = CI.getParentCycle(DivCycle);
922 }
923 LLVM_DEBUG(dbgs() << "\tOuter-most exiting cycle: "
924 << Context.print(CI.getHeader(OuterDivCycle)) << "\n");
925
926 if (!DivergentExitCycles.insert(OuterDivCycle))
927 return;
928
929 // Exit divergence does not matter if the cycle itself is assumed to
930 // be divergent.
931 for (auto C : AssumedDivergent) {
932 if (CI.contains(C, OuterDivCycle))
933 return;
934 }
935
936 analyzeCycleExitDivergence(OuterDivCycle);
937}
938
939template <typename ContextT>
940void GenericUniformityAnalysisImpl<ContextT>::taintAndPushAllDefs(
941 const BlockT &BB) {
942 LLVM_DEBUG(dbgs() << "taintAndPushAllDefs " << Context.print(&BB) << "\n");
943 for (const auto &I : instrs(BB)) {
944 // Terminators do not produce values; they are divergent only if
945 // the condition is divergent. That is handled when the divergent
946 // condition is placed in the worklist.
947 if (I.isTerminator())
948 break;
949
950 markDivergent(I);
951 }
952}
953
954/// Mark divergent phi nodes in a join block
955template <typename ContextT>
956void GenericUniformityAnalysisImpl<ContextT>::taintAndPushPhiNodes(
957 const BlockT &JoinBlock) {
958 LLVM_DEBUG(dbgs() << "taintAndPushPhiNodes in " << Context.print(&JoinBlock)
959 << "\n");
960 for (const auto &Phi : JoinBlock.phis()) {
961 // FIXME: The non-undef value is not constant per se; it just happens to be
962 // uniform and may not dominate this PHI. So assuming that the same value
963 // reaches along all incoming edges may itself be undefined behaviour. This
964 // particular interpretation of the undef value was added to
965 // DivergenceAnalysis in the following review:
966 //
967 // https://reviews.llvm.org/D19013
968 if (ContextT::isConstantOrUndefValuePhi(Phi))
969 continue;
970 markDivergent(Phi);
971 }
972}
973
974/// Add \p Candidate to \p Cycles if it is not already contained in \p Cycles.
975///
976/// \return true iff \p Candidate was added to \p Cycles.
977template <typename CycleInfoT>
978bool insertIfNotContained(const CycleInfoT &CI, SmallVector<CycleRef> &Cycles,
979 CycleRef Candidate) {
980 if (llvm::any_of(Cycles,
981 [&](CycleRef C) { return CI.contains(C, Candidate); }))
982 return false;
983 Cycles.push_back(Candidate);
984 return true;
985}
986
987/// Return the outermost cycle made divergent by branch outside it.
988///
989/// If two paths that diverged outside an irreducible cycle join
990/// inside that cycle, then that whole cycle is assumed to be
991/// divergent. This does not apply if the cycle is reducible.
992template <typename CycleInfoT, typename BlockT>
993CycleRef getExtDivCycle(const CycleInfoT &CI, CycleRef Cycle,
994 const BlockT *DivTermBlock, const BlockT *JoinBlock) {
995 assert(Cycle);
996 assert(CI.contains(Cycle, JoinBlock));
997
998 if (CI.contains(Cycle, DivTermBlock))
999 return CycleRef();
1000
1001 CycleRef OriginalCycle = Cycle;
1002 CycleRef Parent = CI.getParentCycle(Cycle);
1003 while (Parent && !CI.contains(Parent, DivTermBlock)) {
1004 Cycle = Parent;
1005 Parent = CI.getParentCycle(Cycle);
1006 }
1007
1008 // If the original cycle is not the outermost cycle, then the outermost cycle
1009 // is irreducible. If the outermost cycle were reducible, then external
1010 // diverged paths would not reach the original inner cycle.
1011 (void)OriginalCycle;
1012 assert(Cycle == OriginalCycle || !CI.isReducible(Cycle));
1013
1014 if (CI.isReducible(Cycle)) {
1015 assert(CI.getHeader(Cycle) == JoinBlock);
1016 return CycleRef();
1017 }
1018
1019 LLVM_DEBUG(dbgs() << "cycle made divergent by external branch\n");
1020 return Cycle;
1021}
1022
1023/// Return the outermost cycle made divergent by branch inside it.
1024///
1025/// This checks the "diverged entry" criterion defined in the
1026/// docs/ConvergenceAnalysis.html.
1027template <typename ContextT, typename CycleInfoT, typename BlockT,
1028 typename DominatorTreeT>
1029CycleRef getIntDivCycle(const CycleInfoT &CI, CycleRef Cycle,
1030 const BlockT *DivTermBlock, const BlockT *JoinBlock,
1031 const DominatorTreeT &DT, ContextT &Context) {
1032 LLVM_DEBUG(dbgs() << "examine join " << Context.print(JoinBlock)
1033 << " for internal branch " << Context.print(DivTermBlock)
1034 << "\n");
1035 if (DT.properlyDominates(DivTermBlock, JoinBlock))
1036 return CycleRef();
1037
1038 // Find the smallest common cycle, if one exists.
1039 assert(Cycle && CI.contains(Cycle, JoinBlock));
1040 while (Cycle && !CI.contains(Cycle, DivTermBlock)) {
1041 Cycle = CI.getParentCycle(Cycle);
1042 }
1043 if (!Cycle || CI.isReducible(Cycle))
1044 return CycleRef();
1045
1046 if (DT.properlyDominates(CI.getHeader(Cycle), JoinBlock))
1047 return CycleRef();
1048
1049 LLVM_DEBUG(dbgs() << " header " << Context.print(CI.getHeader(Cycle))
1050 << " does not dominate join\n");
1051
1052 CycleRef Parent = CI.getParentCycle(Cycle);
1053 while (Parent && !DT.properlyDominates(CI.getHeader(Parent), JoinBlock)) {
1054 LLVM_DEBUG(dbgs() << " header " << Context.print(CI.getHeader(Parent))
1055 << " does not dominate join\n");
1056 Cycle = Parent;
1057 Parent = CI.getParentCycle(Parent);
1058 }
1059
1060 LLVM_DEBUG(dbgs() << " cycle made divergent by internal branch\n");
1061 return Cycle;
1062}
1063
1064template <typename ContextT, typename CycleInfoT, typename BlockT,
1065 typename DominatorTreeT>
1066CycleRef
1067getOutermostDivergentCycle(const CycleInfoT &CI, CycleRef Cycle,
1068 const BlockT *DivTermBlock, const BlockT *JoinBlock,
1069 const DominatorTreeT &DT, ContextT &Context) {
1070 if (!Cycle)
1071 return CycleRef();
1072
1073 // First try to expand Cycle to the largest that contains JoinBlock
1074 // but not DivTermBlock.
1075 CycleRef Ext = getExtDivCycle(CI, Cycle, DivTermBlock, JoinBlock);
1076
1077 // Continue expanding to the largest cycle that contains both.
1078 CycleRef Int =
1079 getIntDivCycle(CI, Cycle, DivTermBlock, JoinBlock, DT, Context);
1080
1081 if (Int)
1082 return Int;
1083 return Ext;
1084}
1085
1086template <typename ContextT>
1087bool GenericUniformityAnalysisImpl<ContextT>::isTemporalDivergent(
1088 const BlockT &ObservingBlock, const InstructionT &Def) const {
1089 const BlockT *DefBlock = Def.getParent();
1090 for (auto C = CI.getCycle(DefBlock); C && !CI.contains(C, &ObservingBlock);
1091 C = CI.getParentCycle(C)) {
1092 if (DivergentExitCycles.contains(C)) {
1093 return true;
1094 }
1095 }
1096 return false;
1097}
1098
1099template <typename ContextT>
1101 const InstructionT &Term) {
1102 const auto *DivTermBlock = Term.getParent();
1103 DivergentTermBlocks.insert(DivTermBlock);
1104 LLVM_DEBUG(dbgs() << "analyzeControlDiv " << Context.print(DivTermBlock)
1105 << "\n");
1106
1107 // Don't propagate divergence from unreachable blocks.
1108 if (!DT.isReachableFromEntry(DivTermBlock))
1109 return;
1110
1111 const auto &DivDesc = SDA.getJoinBlocks(DivTermBlock);
1112 SmallVector<CycleRef> DivCycles;
1113
1114 // Iterate over all blocks now reachable by a disjoint path join
1115 for (const auto *JoinBlock : DivDesc.JoinDivBlocks) {
1116 CycleRef C = CI.getCycle(JoinBlock);
1117 LLVM_DEBUG(dbgs() << "visiting join block " << Context.print(JoinBlock)
1118 << "\n");
1119 if (CycleRef Outermost = getOutermostDivergentCycle(
1120 CI, C, DivTermBlock, JoinBlock, DT, Context)) {
1121 LLVM_DEBUG(dbgs() << "found divergent cycle\n");
1122 DivCycles.push_back(Outermost);
1123 continue;
1124 }
1125 taintAndPushPhiNodes(*JoinBlock);
1126 }
1127
1128 // Sort by order of decreasing depth. This allows later cycles to be skipped
1129 // because they are already contained in earlier ones.
1130 llvm::sort(DivCycles, [this](CycleRef A, CycleRef B) {
1131 return CI.getDepth(A) > CI.getDepth(B);
1132 });
1133
1134 // Cycles that are assumed divergent due to the diverged entry
1135 // criterion potentially contain temporal divergence depending on
1136 // the DFS chosen. Conservatively, all values produced in such a
1137 // cycle are assumed divergent. "Cycle invariant" values may be
1138 // assumed uniform, but that requires further analysis.
1139 for (auto C : DivCycles) {
1140 if (!insertIfNotContained(CI, AssumedDivergent, C))
1141 continue;
1142 LLVM_DEBUG(dbgs() << "process divergent cycle\n");
1143 for (const BlockT *BB : CI.getBlocks(C)) {
1144 taintAndPushAllDefs(*BB);
1145 }
1146 }
1147
1148 CycleRef BranchCycle = CI.getCycle(DivTermBlock);
1149 assert(DivDesc.CycleDivBlocks.empty() || BranchCycle);
1150 for (const auto *DivExitBlock : DivDesc.CycleDivBlocks) {
1151 propagateCycleExitDivergence(*DivExitBlock, BranchCycle);
1152 }
1153}
1154
1155template <typename ContextT>
1157 HasBranchDivergence = true;
1158
1159 // All values on the Worklist are divergent.
1160 // Their users may not have been updated yet.
1161 while (!Worklist.empty()) {
1162 const InstructionT *I = Worklist.back();
1163 Worklist.pop_back();
1164
1165 LLVM_DEBUG(dbgs() << "worklist pop: " << Context.print(I) << "\n");
1166
1167 if (I->isTerminator()) {
1169 continue;
1170 }
1171
1172 // propagate value divergence to users
1173 assert(hasDivergentDefs(*I) && "Worklist invariant violated!");
1174 pushUsers(*I);
1175 }
1176}
1177
1178template <typename ContextT>
1183
1184template <typename ContextT>
1186 const InstructionT &Instr) const {
1187 return UniformOverrides.contains(&Instr);
1188}
1189
1190template <typename ContextT>
1195
1196template <typename ContextT>
1198 const DominatorTreeT &DT, const CycleInfoT &CI,
1199 const TargetTransformInfo *TTI) {
1200 DA.reset(new ImplT{DT, CI, TTI});
1201}
1202
1203template <typename ContextT>
1205 // When we print Value, LLVM IR instruction, we want to print extra new line.
1206 // In LLVM IR print function for Value does not print new line at the end.
1207 // In MIR print for MachineInstr prints new line at the end.
1208 constexpr bool IsMIR = std::is_same<InstructionT, MachineInstr>::value;
1209 std::string NewLine = IsMIR ? "" : "\n";
1210
1211 bool FoundDivergence = false;
1212
1213 FoundDivergence |= printDivergentArgs(OS);
1214
1215 if (!AssumedDivergent.empty()) {
1216 FoundDivergence = true;
1217 OS << "CYCLES ASSUMED DIVERGENT:\n";
1218 for (auto C : AssumedDivergent) {
1219 OS << " " << CI.print(C) << '\n';
1220 }
1221 }
1222
1223 if (!DivergentExitCycles.empty()) {
1224 FoundDivergence = true;
1225 OS << "CYCLES WITH DIVERGENT EXIT:\n";
1226 for (auto C : DivergentExitCycles) {
1227 OS << " " << CI.print(C) << '\n';
1228 }
1229 }
1230
1231 if (!TemporalDivergenceList.empty()) {
1232 FoundDivergence = true;
1233 OS << "\nTEMPORAL DIVERGENCE LIST:\n";
1234
1235 for (auto [Val, UseInst, C] : TemporalDivergenceList) {
1236 OS << "Value :" << Context.print(Val) << NewLine
1237 << "Used by :" << Context.print(UseInst) << NewLine
1238 << "Outside cycle :" << CI.print(C) << "\n\n";
1239 }
1240 }
1241
1242 for (auto &Block : F) {
1243 OS << "\nBLOCK " << Context.print(&Block) << '\n';
1244
1245 OS << "DEFINITIONS\n";
1247 Context.appendBlockDefs(Defs, Block);
1248 for (auto Value : Defs) {
1249 if (isDivergent(Value)) {
1250 FoundDivergence = true;
1251 OS << " DIVERGENT: ";
1252 } else {
1253 OS << " ";
1254 }
1255 OS << Context.print(Value) << NewLine;
1256 }
1257
1258 OS << "TERMINATORS\n";
1260 Context.appendBlockTerms(Terms, Block);
1261 bool DivergentTerminators = hasDivergentTerminator(Block);
1262 if (DivergentTerminators)
1263 FoundDivergence = true;
1264 for (auto *T : Terms) {
1265 if (DivergentTerminators)
1266 OS << " DIVERGENT: ";
1267 else
1268 OS << " ";
1269 OS << Context.print(T) << NewLine;
1270 }
1271
1272 OS << "END BLOCK\n";
1273 }
1274
1275 if (!FoundDivergence)
1276 OS << "ALL VALUES UNIFORM\n";
1277}
1278
1279template <typename ContextT>
1283 return make_range(DA->TemporalDivergenceList.begin(),
1284 DA->TemporalDivergenceList.end());
1285}
1286
1287template <typename ContextT>
1288const typename ContextT::FunctionT &
1290 return DA->getFunction();
1291}
1292
1293template <typename ContextT>
1296 return DA->getCycleInfo();
1297}
1298
1299/// Whether \p V is divergent at its definition.
1300/// A default-constructed instance (no analysis computed) reports everything
1301/// as uniform, which is conservatively correct for non-divergent targets.
1302template <typename ContextT>
1304 return DA && DA->isDivergent(V);
1305}
1306
1307template <typename ContextT>
1309 const InstructionT *I) const {
1310 assert(I->isTerminator() && "Expected a terminator instruction!");
1311 return DA && DA->isDivergentTerminator(*I);
1312}
1313
1314template <typename ContextT>
1316 return DA && DA->isDivergentUse(U);
1317}
1318
1319template <typename ContextT>
1321 return DA && DA->hasDivergentTerminator(B);
1322}
1323
1324template <typename ContextT>
1326 if (DA)
1327 DA->forgetValue(V);
1328}
1329
1330/// \brief T helper function for printing.
1331template <typename ContextT>
1333 if (!DA) {
1334 Out << " Uniformity analysis not computed (no branch divergence).\n";
1335 return;
1336 }
1337 DA->print(Out);
1338}
1339
1340template <typename ContextT>
1341void llvm::ModifiedPostOrder<ContextT>::computeStackPO(
1342 SmallVectorImpl<const BlockT *> &Stack, const CycleInfoT &CI, CycleRef C,
1344 LLVM_DEBUG(dbgs() << "inside computeStackPO\n");
1345 while (!Stack.empty()) {
1346 auto *NextBB = Stack.back();
1347 if (Finalized.count(NextBB)) {
1348 Stack.pop_back();
1349 continue;
1350 }
1351 LLVM_DEBUG(dbgs() << " visiting " << CI.getSSAContext().print(NextBB)
1352 << "\n");
1353 CycleRef NestedCycle = CI.getCycle(NextBB);
1354 if (C != NestedCycle &&
1355 (!C || (NestedCycle && CI.contains(C, NestedCycle)))) {
1356 LLVM_DEBUG(dbgs() << " found a cycle\n");
1357 while (CI.getParentCycle(NestedCycle) != C)
1358 NestedCycle = CI.getParentCycle(NestedCycle);
1359
1360 SmallVector<BlockT *, 3> NestedExits;
1361 CI.getExitBlocks(NestedCycle, NestedExits);
1362 bool PushedNodes = false;
1363 for (auto *NestedExitBB : NestedExits) {
1364 LLVM_DEBUG(dbgs() << " examine exit: "
1365 << CI.getSSAContext().print(NestedExitBB) << "\n");
1366 if (C && !CI.contains(C, NestedExitBB))
1367 continue;
1368 if (Finalized.count(NestedExitBB))
1369 continue;
1370 PushedNodes = true;
1371 Stack.push_back(NestedExitBB);
1372 LLVM_DEBUG(dbgs() << " pushed exit: "
1373 << CI.getSSAContext().print(NestedExitBB) << "\n");
1374 }
1375 if (!PushedNodes) {
1376 // All loop exits finalized -> finish this node
1377 Stack.pop_back();
1378 computeCyclePO(CI, NestedCycle, Finalized);
1379 }
1380 continue;
1381 }
1382
1383 LLVM_DEBUG(dbgs() << " no nested cycle, going into DAG\n");
1384 // DAG-style
1385 bool PushedNodes = false;
1386 for (auto *SuccBB : successors(NextBB)) {
1387 LLVM_DEBUG(dbgs() << " examine succ: "
1388 << CI.getSSAContext().print(SuccBB) << "\n");
1389 if (C && !CI.contains(C, SuccBB))
1390 continue;
1391 if (Finalized.count(SuccBB))
1392 continue;
1393 PushedNodes = true;
1394 Stack.push_back(SuccBB);
1395 LLVM_DEBUG(dbgs() << " pushed succ: " << CI.getSSAContext().print(SuccBB)
1396 << "\n");
1397 }
1398 if (!PushedNodes) {
1399 // Never push nodes twice
1400 LLVM_DEBUG(dbgs() << " finishing node: "
1401 << CI.getSSAContext().print(NextBB) << "\n");
1402 Stack.pop_back();
1403 Finalized.insert(NextBB);
1404 appendBlock(*NextBB);
1405 }
1406 }
1407 LLVM_DEBUG(dbgs() << "exited computeStackPO\n");
1408}
1409
1410template <typename ContextT>
1411void ModifiedPostOrder<ContextT>::computeCyclePO(
1412 const CycleInfoT &CI, CycleRef C,
1414 LLVM_DEBUG(dbgs() << "inside computeCyclePO\n");
1416 auto *CycleHeader = CI.getHeader(C);
1417
1418 LLVM_DEBUG(dbgs() << " noted header: "
1419 << CI.getSSAContext().print(CycleHeader) << "\n");
1420 assert(!Finalized.count(CycleHeader));
1421 Finalized.insert(CycleHeader);
1422
1423 // Visit the header last
1424 LLVM_DEBUG(dbgs() << " finishing header: "
1425 << CI.getSSAContext().print(CycleHeader) << "\n");
1426 appendBlock(*CycleHeader, CI.isReducible(C));
1427
1428 // Initialize with immediate successors
1429 for (auto *BB : successors(CycleHeader)) {
1430 LLVM_DEBUG(dbgs() << " examine succ: " << CI.getSSAContext().print(BB)
1431 << "\n");
1432 if (!CI.contains(C, BB))
1433 continue;
1434 if (BB == CycleHeader)
1435 continue;
1436 if (!Finalized.count(BB)) {
1437 LLVM_DEBUG(dbgs() << " pushed succ: " << CI.getSSAContext().print(BB)
1438 << "\n");
1439 Stack.push_back(BB);
1440 }
1441 }
1442
1443 // Compute PO inside region
1444 computeStackPO(Stack, CI, C, Finalized);
1445
1446 LLVM_DEBUG(dbgs() << "exited computeCyclePO\n");
1447}
1448
1449/// \brief Generically compute the modified post order.
1450template <typename ContextT>
1454 auto *F = CI.getFunction();
1455 POIndex.assign(GraphTraits<const FunctionT *>::getMaxNumber(F), InvalidIndex);
1456 Stack.reserve(24); // FIXME made-up number
1457 Stack.push_back(&F->front());
1458 computeStackPO(Stack, CI, CycleRef(), Finalized);
1459}
1460
1461} // namespace llvm
1462
1463#undef DEBUG_TYPE
1464
1465#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.