llvm-project/llvm/lib/Transforms/Coroutines/SuspendCrossingInfo.h
Tyler Nowicki ea2da571c7
[Coroutines] Move the SuspendCrossingInfo analysis helper into its own header/source (#106306)
* 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>
2024-09-09 11:50:27 -04:00

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