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