xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Transforms/Coroutines/SuspendCrossingInfo.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===- SuspendCrossingInfo.cpp - Utility for suspend crossing values ------===//
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 // The SuspendCrossingInfo maintains data that allows to answer a question
9 // whether given two BasicBlocks A and B there is a path from A to B that
10 // passes through a suspend point. Note, SuspendCrossingInfo is invalidated
11 // by changes to the CFG including adding/removing BBs due to its use of BB
12 // ptrs in the BlockToIndexMapping.
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_TRANSFORMS_COROUTINES_SUSPENDCROSSINGINFO_H
16 #define LLVM_TRANSFORMS_COROUTINES_SUSPENDCROSSINGINFO_H
17 
18 #include "llvm/ADT/BitVector.h"
19 #include "llvm/ADT/PostOrderIterator.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/IR/BasicBlock.h"
22 #include "llvm/IR/Function.h"
23 #include "llvm/IR/Instruction.h"
24 #include "llvm/Support/Compiler.h"
25 #include "llvm/Transforms/Coroutines/CoroInstr.h"
26 
27 namespace llvm {
28 
29 class ModuleSlotTracker;
30 
31 // Provides two way mapping between the blocks and numbers.
32 class BlockToIndexMapping {
33   SmallVector<BasicBlock *, 32> V;
34 
35 public:
size()36   size_t size() const { return V.size(); }
37 
BlockToIndexMapping(Function & F)38   BlockToIndexMapping(Function &F) {
39     for (BasicBlock &BB : F)
40       V.push_back(&BB);
41     llvm::sort(V);
42   }
43 
blockToIndex(BasicBlock const * BB)44   size_t blockToIndex(BasicBlock const *BB) const {
45     auto *I = llvm::lower_bound(V, BB);
46     assert(I != V.end() && *I == BB && "BasicBlockNumberng: Unknown block");
47     return I - V.begin();
48   }
49 
indexToBlock(unsigned Index)50   BasicBlock *indexToBlock(unsigned Index) const { return V[Index]; }
51 };
52 
53 // The SuspendCrossingInfo maintains data that allows to answer a question
54 // whether given two BasicBlocks A and B there is a path from A to B that
55 // passes through a suspend point.
56 //
57 // For every basic block 'i' it maintains a BlockData that consists of:
58 //   Consumes:  a bit vector which contains a set of indices of blocks that can
59 //              reach block 'i'. A block can trivially reach itself.
60 //   Kills: a bit vector which contains a set of indices of blocks that can
61 //          reach block 'i' but there is a path crossing a suspend point
62 //          not repeating 'i' (path to 'i' without cycles containing 'i').
63 //   Suspend: a boolean indicating whether block 'i' contains a suspend point.
64 //   End: a boolean indicating whether block 'i' contains a coro.end intrinsic.
65 //   KillLoop: There is a path from 'i' to 'i' not otherwise repeating 'i' that
66 //             crosses a suspend point.
67 //
68 class SuspendCrossingInfo {
69   BlockToIndexMapping Mapping;
70 
71   struct BlockData {
72     BitVector Consumes;
73     BitVector Kills;
74     bool Suspend = false;
75     bool End = false;
76     bool KillLoop = false;
77     bool Changed = false;
78   };
79   SmallVector<BlockData, 32> Block;
80 
predecessors(BlockData const & BD)81   iterator_range<pred_iterator> predecessors(BlockData const &BD) const {
82     BasicBlock *BB = Mapping.indexToBlock(&BD - &Block[0]);
83     return llvm::predecessors(BB);
84   }
85 
getBlockData(BasicBlock * BB)86   BlockData &getBlockData(BasicBlock *BB) {
87     return Block[Mapping.blockToIndex(BB)];
88   }
89 
90   /// Compute the BlockData for the current function in one iteration.
91   /// Initialize - Whether this is the first iteration, we can optimize
92   /// the initial case a little bit by manual loop switch.
93   /// Returns whether the BlockData changes in this iteration.
94   template <bool Initialize = false>
95   bool computeBlockData(const ReversePostOrderTraversal<Function *> &RPOT);
96 
97 public:
98 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
99   // Print order is in RPO
100   void dump() const;
101   void dump(StringRef Label, BitVector const &BV,
102             const ReversePostOrderTraversal<Function *> &RPOT,
103             ModuleSlotTracker &MST) const;
104 #endif
105 
106   LLVM_ABI
107   SuspendCrossingInfo(Function &F,
108                       const SmallVectorImpl<AnyCoroSuspendInst *> &CoroSuspends,
109                       const SmallVectorImpl<AnyCoroEndInst *> &CoroEnds);
110 
111   /// Returns true if there is a path from \p From to \p To crossing a suspend
112   /// point without crossing \p From a 2nd time.
113   LLVM_ABI bool hasPathCrossingSuspendPoint(BasicBlock *From,
114                                             BasicBlock *To) const;
115 
116   /// Returns true if there is a path from \p From to \p To crossing a suspend
117   /// point without crossing \p From a 2nd time. If \p From is the same as \p To
118   /// this will also check if there is a looping path crossing a suspend point.
119   LLVM_ABI bool hasPathOrLoopCrossingSuspendPoint(BasicBlock *From,
120                                                   BasicBlock *To) const;
121 
isDefinitionAcrossSuspend(BasicBlock * DefBB,User * U)122   bool isDefinitionAcrossSuspend(BasicBlock *DefBB, User *U) const {
123     auto *I = cast<Instruction>(U);
124 
125     // We rewrote PHINodes, so that only the ones with exactly one incoming
126     // value need to be analyzed.
127     if (auto *PN = dyn_cast<PHINode>(I))
128       if (PN->getNumIncomingValues() > 1)
129         return false;
130 
131     BasicBlock *UseBB = I->getParent();
132 
133     // As a special case, treat uses by an llvm.coro.suspend.retcon or an
134     // llvm.coro.suspend.async as if they were uses in the suspend's single
135     // predecessor: the uses conceptually occur before the suspend.
136     if (isa<CoroSuspendRetconInst>(I) || isa<CoroSuspendAsyncInst>(I)) {
137       UseBB = UseBB->getSinglePredecessor();
138       assert(UseBB && "should have split coro.suspend into its own block");
139     }
140 
141     return hasPathCrossingSuspendPoint(DefBB, UseBB);
142   }
143 
isDefinitionAcrossSuspend(Argument & A,User * U)144   bool isDefinitionAcrossSuspend(Argument &A, User *U) const {
145     return isDefinitionAcrossSuspend(&A.getParent()->getEntryBlock(), U);
146   }
147 
isDefinitionAcrossSuspend(Instruction & I,User * U)148   bool isDefinitionAcrossSuspend(Instruction &I, User *U) const {
149     auto *DefBB = I.getParent();
150 
151     // As a special case, treat values produced by an llvm.coro.suspend.*
152     // as if they were defined in the single successor: the uses
153     // conceptually occur after the suspend.
154     if (isa<AnyCoroSuspendInst>(I)) {
155       DefBB = DefBB->getSingleSuccessor();
156       assert(DefBB && "should have split coro.suspend into its own block");
157     }
158 
159     return isDefinitionAcrossSuspend(DefBB, U);
160   }
161 
isDefinitionAcrossSuspend(Value & V,User * U)162   bool isDefinitionAcrossSuspend(Value &V, User *U) const {
163     if (auto *Arg = dyn_cast<Argument>(&V))
164       return isDefinitionAcrossSuspend(*Arg, U);
165     if (auto *Inst = dyn_cast<Instruction>(&V))
166       return isDefinitionAcrossSuspend(*Inst, U);
167 
168     llvm_unreachable(
169         "Coroutine could only collect Argument and Instruction now.");
170   }
171 
isDefinitionAcrossSuspend(Value & V)172   bool isDefinitionAcrossSuspend(Value &V) const {
173     if (auto *Arg = dyn_cast<Argument>(&V)) {
174       for (User *U : Arg->users())
175         if (isDefinitionAcrossSuspend(*Arg, U))
176           return true;
177     } else if (auto *Inst = dyn_cast<Instruction>(&V)) {
178       for (User *U : Inst->users())
179         if (isDefinitionAcrossSuspend(*Inst, U))
180           return true;
181     }
182 
183     llvm_unreachable(
184         "Coroutine could only collect Argument and Instruction now.");
185   }
186 };
187 
188 } // namespace llvm
189 
190 #endif // LLVM_TRANSFORMS_COROUTINES_SUSPENDCROSSINGINFO_H
191