
* Move the SuspendCrossingInfo analysis helper into its own header/source See RFC for more info: https://discourse.llvm.org/t/rfc-abi-objects-for-coroutines/81057 Co-authored-by: tnowicki <tnowicki.nowicki@amd.com>
185 lines
6.8 KiB
C++
185 lines
6.8 KiB
C++
//===- SuspendCrossingInfo.cpp - Utility for suspend crossing values ------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
// The SuspendCrossingInfo maintains data that allows to answer a question
|
|
// whether given two BasicBlocks A and B there is a path from A to B that
|
|
// passes through a suspend point. Note, SuspendCrossingInfo is invalidated
|
|
// by changes to the CFG including adding/removing BBs due to its use of BB
|
|
// ptrs in the BlockToIndexMapping.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_LIB_TRANSFORMS_COROUTINES_SUSPENDCROSSINGINFO_H
|
|
#define LLVM_LIB_TRANSFORMS_COROUTINES_SUSPENDCROSSINGINFO_H
|
|
|
|
#include "CoroInstr.h"
|
|
#include "llvm/ADT/BitVector.h"
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/IR/BasicBlock.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Instruction.h"
|
|
|
|
namespace llvm {
|
|
|
|
// Provides two way mapping between the blocks and numbers.
|
|
class BlockToIndexMapping {
|
|
SmallVector<BasicBlock *, 32> V;
|
|
|
|
public:
|
|
size_t size() const { return V.size(); }
|
|
|
|
BlockToIndexMapping(Function &F) {
|
|
for (BasicBlock &BB : F)
|
|
V.push_back(&BB);
|
|
llvm::sort(V);
|
|
}
|
|
|
|
size_t blockToIndex(BasicBlock const *BB) const {
|
|
auto *I = llvm::lower_bound(V, BB);
|
|
assert(I != V.end() && *I == BB && "BasicBlockNumberng: Unknown block");
|
|
return I - V.begin();
|
|
}
|
|
|
|
BasicBlock *indexToBlock(unsigned Index) const { return V[Index]; }
|
|
};
|
|
|
|
// The SuspendCrossingInfo maintains data that allows to answer a question
|
|
// whether given two BasicBlocks A and B there is a path from A to B that
|
|
// passes through a suspend point.
|
|
//
|
|
// For every basic block 'i' it maintains a BlockData that consists of:
|
|
// Consumes: a bit vector which contains a set of indices of blocks that can
|
|
// reach block 'i'. A block can trivially reach itself.
|
|
// Kills: a bit vector which contains a set of indices of blocks that can
|
|
// reach block 'i' but there is a path crossing a suspend point
|
|
// not repeating 'i' (path to 'i' without cycles containing 'i').
|
|
// Suspend: a boolean indicating whether block 'i' contains a suspend point.
|
|
// End: a boolean indicating whether block 'i' contains a coro.end intrinsic.
|
|
// KillLoop: There is a path from 'i' to 'i' not otherwise repeating 'i' that
|
|
// crosses a suspend point.
|
|
//
|
|
class SuspendCrossingInfo {
|
|
BlockToIndexMapping Mapping;
|
|
|
|
struct BlockData {
|
|
BitVector Consumes;
|
|
BitVector Kills;
|
|
bool Suspend = false;
|
|
bool End = false;
|
|
bool KillLoop = false;
|
|
bool Changed = false;
|
|
};
|
|
SmallVector<BlockData, 32> Block;
|
|
|
|
iterator_range<pred_iterator> predecessors(BlockData const &BD) const {
|
|
BasicBlock *BB = Mapping.indexToBlock(&BD - &Block[0]);
|
|
return llvm::predecessors(BB);
|
|
}
|
|
|
|
BlockData &getBlockData(BasicBlock *BB) {
|
|
return Block[Mapping.blockToIndex(BB)];
|
|
}
|
|
|
|
/// Compute the BlockData for the current function in one iteration.
|
|
/// Initialize - Whether this is the first iteration, we can optimize
|
|
/// the initial case a little bit by manual loop switch.
|
|
/// Returns whether the BlockData changes in this iteration.
|
|
template <bool Initialize = false>
|
|
bool computeBlockData(const ReversePostOrderTraversal<Function *> &RPOT);
|
|
|
|
public:
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
// Print order is in RPO
|
|
void dump() const;
|
|
void dump(StringRef Label, BitVector const &BV,
|
|
const ReversePostOrderTraversal<Function *> &RPOT) const;
|
|
#endif
|
|
|
|
SuspendCrossingInfo(Function &F,
|
|
const SmallVectorImpl<AnyCoroSuspendInst *> &CoroSuspends,
|
|
const SmallVectorImpl<AnyCoroEndInst *> &CoroEnds);
|
|
|
|
/// Returns true if there is a path from \p From to \p To crossing a suspend
|
|
/// point without crossing \p From a 2nd time.
|
|
bool hasPathCrossingSuspendPoint(BasicBlock *From, BasicBlock *To) const;
|
|
|
|
/// Returns true if there is a path from \p From to \p To crossing a suspend
|
|
/// point without crossing \p From a 2nd time. If \p From is the same as \p To
|
|
/// this will also check if there is a looping path crossing a suspend point.
|
|
bool hasPathOrLoopCrossingSuspendPoint(BasicBlock *From,
|
|
BasicBlock *To) const;
|
|
|
|
bool isDefinitionAcrossSuspend(BasicBlock *DefBB, User *U) const {
|
|
auto *I = cast<Instruction>(U);
|
|
|
|
// We rewrote PHINodes, so that only the ones with exactly one incoming
|
|
// value need to be analyzed.
|
|
if (auto *PN = dyn_cast<PHINode>(I))
|
|
if (PN->getNumIncomingValues() > 1)
|
|
return false;
|
|
|
|
BasicBlock *UseBB = I->getParent();
|
|
|
|
// As a special case, treat uses by an llvm.coro.suspend.retcon or an
|
|
// llvm.coro.suspend.async as if they were uses in the suspend's single
|
|
// predecessor: the uses conceptually occur before the suspend.
|
|
if (isa<CoroSuspendRetconInst>(I) || isa<CoroSuspendAsyncInst>(I)) {
|
|
UseBB = UseBB->getSinglePredecessor();
|
|
assert(UseBB && "should have split coro.suspend into its own block");
|
|
}
|
|
|
|
return hasPathCrossingSuspendPoint(DefBB, UseBB);
|
|
}
|
|
|
|
bool isDefinitionAcrossSuspend(Argument &A, User *U) const {
|
|
return isDefinitionAcrossSuspend(&A.getParent()->getEntryBlock(), U);
|
|
}
|
|
|
|
bool isDefinitionAcrossSuspend(Instruction &I, User *U) const {
|
|
auto *DefBB = I.getParent();
|
|
|
|
// As a special case, treat values produced by an llvm.coro.suspend.*
|
|
// as if they were defined in the single successor: the uses
|
|
// conceptually occur after the suspend.
|
|
if (isa<AnyCoroSuspendInst>(I)) {
|
|
DefBB = DefBB->getSingleSuccessor();
|
|
assert(DefBB && "should have split coro.suspend into its own block");
|
|
}
|
|
|
|
return isDefinitionAcrossSuspend(DefBB, U);
|
|
}
|
|
|
|
bool isDefinitionAcrossSuspend(Value &V, User *U) const {
|
|
if (auto *Arg = dyn_cast<Argument>(&V))
|
|
return isDefinitionAcrossSuspend(*Arg, U);
|
|
if (auto *Inst = dyn_cast<Instruction>(&V))
|
|
return isDefinitionAcrossSuspend(*Inst, U);
|
|
|
|
llvm_unreachable(
|
|
"Coroutine could only collect Argument and Instruction now.");
|
|
}
|
|
|
|
bool isDefinitionAcrossSuspend(Value &V) const {
|
|
if (auto *Arg = dyn_cast<Argument>(&V)) {
|
|
for (User *U : Arg->users())
|
|
if (isDefinitionAcrossSuspend(*Arg, U))
|
|
return true;
|
|
} else if (auto *Inst = dyn_cast<Instruction>(&V)) {
|
|
for (User *U : Inst->users())
|
|
if (isDefinitionAcrossSuspend(*Inst, U))
|
|
return true;
|
|
}
|
|
|
|
llvm_unreachable(
|
|
"Coroutine could only collect Argument and Instruction now.");
|
|
}
|
|
};
|
|
|
|
} // namespace llvm
|
|
|
|
#endif // LLVM_TRANSFORMS_COROUTINES_SUSPENDCROSSINGINFO_H
|