#ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
#define LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
#include "CoroInstr.h"
#include "llvm/IR/IRBuilder.h"
namespace llvm {
class CallGraph;
namespace coro {
bool declaresAnyIntrinsic(const Module &M);
bool declaresIntrinsics(const Module &M,
const std::initializer_list<StringRef>);
void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
void salvageDebugInfo(
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
DbgVariableIntrinsic *DVI, bool OptimizeFrame);
struct LowererBase {
Module &TheModule;
LLVMContext &Context;
PointerType *const Int8Ptr;
FunctionType *const ResumeFnType;
ConstantPointerNull *const NullPtr;
LowererBase(Module &M);
Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt);
};
enum class ABI {
Switch,
Retcon,
RetconOnce,
Async,
};
struct LLVM_LIBRARY_VISIBILITY Shape {
CoroBeginInst *CoroBegin;
SmallVector<AnyCoroEndInst *, 4> CoroEnds;
SmallVector<CoroSizeInst *, 2> CoroSizes;
SmallVector<CoroAlignInst *, 2> CoroAligns;
SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
SmallVector<CallInst*, 2> SwiftErrorOps;
struct SwitchFieldIndex {
enum {
Resume,
Destroy
};
};
coro::ABI ABI;
StructType *FrameTy;
Align FrameAlign;
uint64_t FrameSize;
Value *FramePtr;
BasicBlock *AllocaSpillBlock;
bool OptimizeFrame;
struct SwitchLoweringStorage {
SwitchInst *ResumeSwitch;
AllocaInst *PromiseAlloca;
BasicBlock *ResumeEntryBlock;
unsigned IndexField;
unsigned IndexAlign;
unsigned IndexOffset;
bool HasFinalSuspend;
};
struct RetconLoweringStorage {
Function *ResumePrototype;
Function *Alloc;
Function *Dealloc;
BasicBlock *ReturnBlock;
bool IsFrameInlineInStorage;
};
struct AsyncLoweringStorage {
FunctionType *AsyncFuncTy;
Value *Context;
CallingConv::ID AsyncCC;
unsigned ContextArgNo;
uint64_t ContextHeaderSize;
uint64_t ContextAlignment;
uint64_t FrameOffset; uint64_t ContextSize; GlobalVariable *AsyncFuncPointer;
Align getContextAlignment() const { return Align(ContextAlignment); }
};
union {
SwitchLoweringStorage SwitchLowering;
RetconLoweringStorage RetconLowering;
AsyncLoweringStorage AsyncLowering;
};
CoroIdInst *getSwitchCoroId() const {
assert(ABI == coro::ABI::Switch);
return cast<CoroIdInst>(CoroBegin->getId());
}
AnyCoroIdRetconInst *getRetconCoroId() const {
assert(ABI == coro::ABI::Retcon ||
ABI == coro::ABI::RetconOnce);
return cast<AnyCoroIdRetconInst>(CoroBegin->getId());
}
CoroIdAsyncInst *getAsyncCoroId() const {
assert(ABI == coro::ABI::Async);
return cast<CoroIdAsyncInst>(CoroBegin->getId());
}
unsigned getSwitchIndexField() const {
assert(ABI == coro::ABI::Switch);
assert(FrameTy && "frame type not assigned");
return SwitchLowering.IndexField;
}
IntegerType *getIndexType() const {
assert(ABI == coro::ABI::Switch);
assert(FrameTy && "frame type not assigned");
return cast<IntegerType>(FrameTy->getElementType(getSwitchIndexField()));
}
ConstantInt *getIndex(uint64_t Value) const {
return ConstantInt::get(getIndexType(), Value);
}
PointerType *getSwitchResumePointerType() const {
assert(ABI == coro::ABI::Switch);
assert(FrameTy && "frame type not assigned");
return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume));
}
FunctionType *getResumeFunctionType() const {
switch (ABI) {
case coro::ABI::Switch:
return FunctionType::get(Type::getVoidTy(FrameTy->getContext()),
FrameTy->getPointerTo(), false);
case coro::ABI::Retcon:
case coro::ABI::RetconOnce:
return RetconLowering.ResumePrototype->getFunctionType();
case coro::ABI::Async:
return nullptr;
}
llvm_unreachable("Unknown coro::ABI enum");
}
ArrayRef<Type*> getRetconResultTypes() const {
assert(ABI == coro::ABI::Retcon ||
ABI == coro::ABI::RetconOnce);
auto FTy = CoroBegin->getFunction()->getFunctionType();
if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) {
return STy->elements().slice(1);
} else {
return ArrayRef<Type*>();
}
}
ArrayRef<Type*> getRetconResumeTypes() const {
assert(ABI == coro::ABI::Retcon ||
ABI == coro::ABI::RetconOnce);
auto FTy = RetconLowering.ResumePrototype->getFunctionType();
return FTy->params().slice(1);
}
CallingConv::ID getResumeFunctionCC() const {
switch (ABI) {
case coro::ABI::Switch:
return CallingConv::Fast;
case coro::ABI::Retcon:
case coro::ABI::RetconOnce:
return RetconLowering.ResumePrototype->getCallingConv();
case coro::ABI::Async:
return AsyncLowering.AsyncCC;
}
llvm_unreachable("Unknown coro::ABI enum");
}
AllocaInst *getPromiseAlloca() const {
if (ABI == coro::ABI::Switch)
return SwitchLowering.PromiseAlloca;
return nullptr;
}
Instruction *getInsertPtAfterFramePtr() const {
if (auto *I = dyn_cast<Instruction>(FramePtr))
return I->getNextNode();
return &cast<Argument>(FramePtr)->getParent()->getEntryBlock().front();
}
Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const;
void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const;
Shape() = default;
explicit Shape(Function &F, bool OptimizeFrame = false)
: OptimizeFrame(OptimizeFrame) {
buildFrom(F);
}
void buildFrom(Function &F);
};
void buildCoroutineFrame(Function &F, Shape &Shape);
CallInst *createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
ArrayRef<Value *> Arguments, IRBuilder<> &);
} }
#endif