24#define DEBUG_TYPE "coro-elide"
26STATISTIC(NumOfCoroElided,
"The # of coroutine get elided.");
36class FunctionElideInfo {
38 FunctionElideInfo(
Function *
F) : ContainingFunction(
F) {
39 this->collectPostSplitCoroIds();
42 bool hasCoroIds()
const {
return !CoroIds.empty(); }
44 const SmallVectorImpl<CoroIdInst *> &getCoroIds()
const {
return CoroIds; }
50 SmallPtrSet<const SwitchInst *, 4> CoroSuspendSwitches;
52 void collectPostSplitCoroIds();
53 friend class CoroIdElider;
58 CoroIdElider(CoroIdInst *CoroId, FunctionElideInfo &FEI, AAResults &AA,
59 DominatorTree &DT, OptimizationRemarkEmitter &ORE);
60 void elideHeapAllocations(uint64_t FrameSize, Align FrameAlign);
61 bool lifetimeEligibleForElide()
const;
63 bool canCoroBeginEscape(
const CoroBeginInst *,
64 const SmallPtrSetImpl<BasicBlock *> &)
const;
68 FunctionElideInfo &FEI;
71 OptimizationRemarkEmitter &ORE;
77 DenseMap<CoroBeginInst *, SmallVector<IntrinsicInst *, 4>> BeginDeadMap;
92 if (
Op->getType()->isPointerTy() && !
AA.isNoAlias(
Op, Frame))
109 !
Call->isMustTailCall())
110 Call->setTailCall(
false);
115static std::optional<std::pair<uint64_t, Align>>
135 "coro-elide-info-output-file shouldn't be empty");
141 llvm::errs() <<
"Error opening coro-elide-info-output-file '"
143 return std::make_unique<raw_fd_ostream>(2,
false);
147void FunctionElideInfo::collectPostSplitCoroIds() {
150 if (CII->getInfo().isPostSplit())
151 CoroIds.push_back(CII);
159 if (CSI->hasOneUse() &&
isa<SwitchInst>(CSI->use_begin()->getUser())) {
162 CoroSuspendSwitches.insert(SWI);
167CoroIdElider::CoroIdElider(CoroIdInst *CoroId, FunctionElideInfo &FEI,
168 AAResults &AA, DominatorTree &DT,
169 OptimizationRemarkEmitter &ORE)
170 : CoroId(CoroId), FEI(FEI), AA(AA), DT(DT), ORE(ORE) {
173 if (auto *CB = dyn_cast<CoroBeginInst>(U))
174 CoroBegins.push_back(CB);
175 else if (auto *CA = dyn_cast<CoroAllocInst>(U))
176 CoroAllocs.push_back(CA);
181 auto &CoroDeads = BeginDeadMap[CB];
187 switch (
II->getIndex()) {
189 ResumeAddr.push_back(
II);
192 CoroDeads.push_back(
II);
193 DestroyAddr.push_back(
II);
199 CoroDeads.push_back(
II);
206void CoroIdElider::elideHeapAllocations(
uint64_t FrameSize,
Align FrameAlign) {
207 LLVMContext &
C = FEI.ContainingFunction->
getContext();
218 for (
auto *CA : CoroAllocs) {
219 CA->replaceAllUsesWith(False);
220 CA->eraseFromParent();
228 auto FrameTy = ArrayType::get(Type::getInt8Ty(
C), FrameSize);
229 auto *Frame =
new AllocaInst(FrameTy,
DL.getAllocaAddrSpace(),
"", InsertPt);
230 Frame->setAlignment(FrameAlign);
232 new BitCastInst(Frame, PointerType::getUnqual(
C),
"vFrame", InsertPt);
234 for (
auto *CB : CoroBegins) {
236 CB->replaceAllUsesWith(FrameVoidPtr);
237 CB->eraseFromParent();
245bool CoroIdElider::canCoroBeginEscape(
246 const CoroBeginInst *CB,
const SmallPtrSetImpl<BasicBlock *> &TIs)
const {
247 const auto &It = BeginDeadMap.find(CB);
248 assert(It != BeginDeadMap.end());
251 unsigned Limit = 32 * (1 + It->second.size());
256 SmallPtrSet<const BasicBlock *, 32> Visited;
259 for (
auto *DA : It->second)
262 SmallPtrSet<const BasicBlock *, 32> EscapingBBs;
263 for (
auto *U : CB->
users()) {
282 bool PotentiallyEscaped =
false;
286 if (!Visited.
insert(BB).second)
292 PotentiallyEscaped |= EscapingBBs.
count(BB);
310 auto TI = BB->getTerminator();
321 }
while (!Worklist.
empty());
328bool CoroIdElider::lifetimeEligibleForElide()
const {
331 if (CoroAllocs.empty())
341 SmallPtrSet<BasicBlock *, 8> Terminators;
345 for (BasicBlock &
B : *FEI.ContainingFunction) {
346 auto *TI =
B.getTerminator();
355 for (
const auto *CB : CoroBegins) {
356 auto It = BeginDeadMap.find(CB);
357 if (It == BeginDeadMap.end())
362 auto DominatesTerminator = [&](
auto *TI) {
364 return DT.dominates(Destroy, TI->getTerminator());
377 if (canCoroBeginEscape(CB, Terminators))
386bool CoroIdElider::attemptElide() {
390 assert(Resumers &&
"PostSplit coro.id Info argument must refer to an array"
391 "of coroutine subfunctions");
392 auto *ResumeAddrConstant =
397 bool EligibleForElide = lifetimeEligibleForElide();
407 auto CallerFunctionName = FEI.ContainingFunction->
getName();
410 if (EligibleForElide && FrameSizeAndAlign) {
411 elideHeapAllocations(FrameSizeAndAlign->first, FrameSizeAndAlign->second);
417 << FEI.ContainingFunction->
getName() <<
"\n";
421 return OptimizationRemark(
DEBUG_TYPE,
"CoroElide", CoroId)
422 <<
"'" <<
ore::NV(
"callee", CalleeCoroutineName)
423 <<
"' elided in '" <<
ore::NV(
"caller", CallerFunctionName)
425 <<
ore::NV(
"frame_size", FrameSizeAndAlign->first) <<
", align="
426 <<
ore::NV(
"align", FrameSizeAndAlign->second.value()) <<
")";
431 <<
"'" <<
ore::NV(
"callee", CalleeCoroutineName)
432 <<
"' not elided in '"
433 <<
ore::NV(
"caller", CallerFunctionName);
435 if (FrameSizeAndAlign)
436 return Remark <<
"' (frame_size="
437 <<
ore::NV(
"frame_size", FrameSizeAndAlign->first)
439 <<
ore::NV(
"align", FrameSizeAndAlign->second.value())
442 return Remark <<
"' (frame_size=unknown, align=unknown)";
450 auto &M = *
F.getParent();
454 FunctionElideInfo FEI{&
F};
456 if (!FEI.hasCoroIds())
464 for (
auto *CII : FEI.getCoroIds()) {
465 CoroIdElider CIE(CII, FEI,
AA, DT, ORE);
for(const MachineOperand &MO :llvm::drop_begin(OldMI.operands(), Desc.getNumOperands()))
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static bool replaceWithConstant(MachineIRBuilder &B, MachineInstr &MI, int64_t C)
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
static const Function * getParent(const Value *V)
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static void replaceWithConstant(Constant *Value, SmallVectorImpl< CoroSubFnInst * > &Users)
static Instruction * getFirstNonAllocaInTheEntryBlock(Function *F)
static cl::opt< std::string > CoroElideInfoOutputFilename("coro-elide-info-output-file", cl::value_desc("filename"), cl::desc("File to record the coroutines got elided"), cl::Hidden)
static void removeTailCallAttribute(AllocaInst *Frame, AAResults &AA)
static std::optional< std::pair< uint64_t, Align > > getFrameLayout(Function *Resume)
static std::unique_ptr< raw_fd_ostream > getOrCreateLogFile()
static bool operandReferences(CallInst *CI, AllocaInst *Frame, AAResults &AA)
This file defines the DenseMap class.
iv Induction Variable Users
uint64_t IntrinsicInst * II
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
static Instruction * getFirstNonAllocaInTheEntryBlock(Function &F)
A manager for alias analyses.
an instruction to allocate memory on the stack
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
InstListType::iterator iterator
Instruction iterators...
This class represents a function call, abstracting a target machine's calling convention.
static LLVM_ABI ConstantInt * getFalse(LLVMContext &Context)
This is an important base class in LLVM.
LLVM_ABI Constant * getAggregateElement(unsigned Elt) const
For aggregates (struct/array/vector) return the constant that corresponds to the specified element if...
This class represents the llvm.coro.begin or llvm.coro.begin.custom.abi instructions.
Function * getCoroutine() const
This class represents the llvm.coro.subfn.addr instruction.
Analysis pass which computes a DominatorTree.
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
const DataLayout & getDataLayout() const
Get the data layout of the module this function belongs to.
uint64_t getParamDereferenceableBytes(unsigned ArgNo) const
Extract the number of dereferenceable bytes for a parameter.
MaybeAlign getParamAlign(unsigned ArgNo) const
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
LLVM_ABI const Function * getFunction() const
Return the function this instruction belongs to.
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
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.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void push_back(const T &Elt)
unsigned getNumCases() const
Return the number of 'cases' in this switch instruction, excluding the default case.
iterator_range< value_op_iterator > operand_values()
LLVM Value Representation.
iterator_range< user_iterator > users()
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
const ParentTy * getParent() const
self_iterator getIterator()
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Abstract Attribute helper functions.
@ C
The default llvm calling convention, compatible with C.
void elideCoroFree(Value *FramePtr)
bool declaresIntrinsics(const Module &M, ArrayRef< Intrinsic::ID > List)
DiagnosticInfoOptimizationBase::Argument NV
@ OF_Append
The file should be opened in append mode.
This is an optimization pass for GlobalISel generic memory operations.
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
LLVM_ABI bool replaceAndRecursivelySimplify(Instruction *I, Value *SimpleV, const TargetLibraryInfo *TLI=nullptr, const DominatorTree *DT=nullptr, AssumptionCache *AC=nullptr, SmallSetVector< Instruction *, 8 > *UnsimplifiedUsers=nullptr)
Replace all uses of 'I' with 'SimpleV' and simplify the uses recursively.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
RNSuccIterator< NodeRef, BlockT, RegionT > succ_begin(NodeRef Node)
RNSuccIterator< NodeRef, BlockT, RegionT > succ_end(NodeRef Node)
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
This struct is a compact representation of a valid (non-zero power of two) alignment.
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)
Align valueOrOne() const
For convenience, returns a valid alignment or 1 if undefined.