#include "CoroInternal.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Analysis/PtrUseVisitor.h"
#include "llvm/Analysis/StackLifetime.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/OptimizedStructLayout.h"
#include "llvm/Support/circular_raw_ostream.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#include <algorithm>
using namespace llvm;
#define DEBUG_TYPE "coro-suspend-crossing"
enum { SmallVectorThreshold = 32 };
namespace {
class BlockToIndexMapping {
SmallVector<BasicBlock *, SmallVectorThreshold> 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 *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]; }
};
}
namespace {
struct SuspendCrossingInfo {
BlockToIndexMapping Mapping;
struct BlockData {
BitVector Consumes;
BitVector Kills;
bool Suspend = false;
bool End = false;
};
SmallVector<BlockData, SmallVectorThreshold> Block;
iterator_range<succ_iterator> successors(BlockData const &BD) const {
BasicBlock *BB = Mapping.indexToBlock(&BD - &Block[0]);
return llvm::successors(BB);
}
BlockData &getBlockData(BasicBlock *BB) {
return Block[Mapping.blockToIndex(BB)];
}
void dump() const;
void dump(StringRef Label, BitVector const &BV) const;
SuspendCrossingInfo(Function &F, coro::Shape &Shape);
bool hasPathCrossingSuspendPoint(BasicBlock *DefBB, BasicBlock *UseBB) const {
size_t const DefIndex = Mapping.blockToIndex(DefBB);
size_t const UseIndex = Mapping.blockToIndex(UseBB);
bool const Result = Block[UseIndex].Kills[DefIndex];
LLVM_DEBUG(dbgs() << UseBB->getName() << " => " << DefBB->getName()
<< " answer is " << Result << "\n");
return Result;
}
bool isDefinitionAcrossSuspend(BasicBlock *DefBB, User *U) const {
auto *I = cast<Instruction>(U);
if (auto *PN = dyn_cast<PHINode>(I))
if (PN->getNumIncomingValues() > 1)
return false;
BasicBlock *UseBB = I->getParent();
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();
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.");
}
};
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void SuspendCrossingInfo::dump(StringRef Label,
BitVector const &BV) const {
dbgs() << Label << ":";
for (size_t I = 0, N = BV.size(); I < N; ++I)
if (BV[I])
dbgs() << " " << Mapping.indexToBlock(I)->getName();
dbgs() << "\n";
}
LLVM_DUMP_METHOD void SuspendCrossingInfo::dump() const {
for (size_t I = 0, N = Block.size(); I < N; ++I) {
BasicBlock *const B = Mapping.indexToBlock(I);
dbgs() << B->getName() << ":\n";
dump(" Consumes", Block[I].Consumes);
dump(" Kills", Block[I].Kills);
}
dbgs() << "\n";
}
#endif
SuspendCrossingInfo::SuspendCrossingInfo(Function &F, coro::Shape &Shape)
: Mapping(F) {
const size_t N = Mapping.size();
Block.resize(N);
for (size_t I = 0; I < N; ++I) {
auto &B = Block[I];
B.Consumes.resize(N);
B.Kills.resize(N);
B.Consumes.set(I);
}
for (auto *CE : Shape.CoroEnds)
getBlockData(CE->getParent()).End = true;
auto markSuspendBlock = [&](IntrinsicInst *BarrierInst) {
BasicBlock *SuspendBlock = BarrierInst->getParent();
auto &B = getBlockData(SuspendBlock);
B.Suspend = true;
B.Kills |= B.Consumes;
};
for (auto *CSI : Shape.CoroSuspends) {
markSuspendBlock(CSI);
if (auto *Save = CSI->getCoroSave())
markSuspendBlock(Save);
}
int Iteration = 0;
(void)Iteration;
bool Changed;
do {
LLVM_DEBUG(dbgs() << "iteration " << ++Iteration);
LLVM_DEBUG(dbgs() << "==============\n");
Changed = false;
for (size_t I = 0; I < N; ++I) {
auto &B = Block[I];
for (BasicBlock *SI : successors(B)) {
auto SuccNo = Mapping.blockToIndex(SI);
auto &S = Block[SuccNo];
auto SavedConsumes = S.Consumes;
auto SavedKills = S.Kills;
S.Consumes |= B.Consumes;
S.Kills |= B.Kills;
if (B.Suspend) {
S.Kills |= B.Consumes;
}
if (S.Suspend) {
S.Kills |= S.Consumes;
} else if (S.End) {
S.Kills.reset();
} else {
S.Kills.reset(SuccNo);
}
Changed |= (S.Kills != SavedKills) || (S.Consumes != SavedConsumes);
if (S.Kills != SavedKills) {
LLVM_DEBUG(dbgs() << "\nblock " << I << " follower " << SI->getName()
<< "\n");
LLVM_DEBUG(dump("S.Kills", S.Kills));
LLVM_DEBUG(dump("SavedKills", SavedKills));
}
if (S.Consumes != SavedConsumes) {
LLVM_DEBUG(dbgs() << "\nblock " << I << " follower " << SI << "\n");
LLVM_DEBUG(dump("S.Consume", S.Consumes));
LLVM_DEBUG(dump("SavedCons", SavedConsumes));
}
}
}
} while (Changed);
LLVM_DEBUG(dump());
}
#undef DEBUG_TYPE
#define DEBUG_TYPE "coro-frame"
namespace {
class FrameTypeBuilder;
using SpillInfo = SmallMapVector<Value *, SmallVector<Instruction *, 2>, 8>;
struct AllocaInfo {
AllocaInst *Alloca;
DenseMap<Instruction *, llvm::Optional<APInt>> Aliases;
bool MayWriteBeforeCoroBegin;
AllocaInfo(AllocaInst *Alloca,
DenseMap<Instruction *, llvm::Optional<APInt>> Aliases,
bool MayWriteBeforeCoroBegin)
: Alloca(Alloca), Aliases(std::move(Aliases)),
MayWriteBeforeCoroBegin(MayWriteBeforeCoroBegin) {}
};
struct FrameDataInfo {
SpillInfo Spills;
SmallVector<AllocaInfo, 8> Allocas;
SmallVector<Value *, 8> getAllDefs() const {
SmallVector<Value *, 8> Defs;
for (const auto &P : Spills)
Defs.push_back(P.first);
for (const auto &A : Allocas)
Defs.push_back(A.Alloca);
return Defs;
}
uint32_t getFieldIndex(Value *V) const {
auto Itr = FieldIndexMap.find(V);
assert(Itr != FieldIndexMap.end() &&
"Value does not have a frame field index");
return Itr->second;
}
void setFieldIndex(Value *V, uint32_t Index) {
assert((LayoutIndexUpdateStarted || FieldIndexMap.count(V) == 0) &&
"Cannot set the index for the same field twice.");
FieldIndexMap[V] = Index;
}
Align getAlign(Value *V) const {
auto Iter = FieldAlignMap.find(V);
assert(Iter != FieldAlignMap.end());
return Iter->second;
}
void setAlign(Value *V, Align AL) {
assert(FieldAlignMap.count(V) == 0);
FieldAlignMap.insert({V, AL});
}
uint64_t getDynamicAlign(Value *V) const {
auto Iter = FieldDynamicAlignMap.find(V);
assert(Iter != FieldDynamicAlignMap.end());
return Iter->second;
}
void setDynamicAlign(Value *V, uint64_t Align) {
assert(FieldDynamicAlignMap.count(V) == 0);
FieldDynamicAlignMap.insert({V, Align});
}
uint64_t getOffset(Value *V) const {
auto Iter = FieldOffsetMap.find(V);
assert(Iter != FieldOffsetMap.end());
return Iter->second;
}
void setOffset(Value *V, uint64_t Offset) {
assert(FieldOffsetMap.count(V) == 0);
FieldOffsetMap.insert({V, Offset});
}
void updateLayoutIndex(FrameTypeBuilder &B);
private:
bool LayoutIndexUpdateStarted = false;
DenseMap<Value *, uint32_t> FieldIndexMap;
DenseMap<Value *, Align> FieldAlignMap;
DenseMap<Value *, uint64_t> FieldDynamicAlignMap;
DenseMap<Value *, uint64_t> FieldOffsetMap;
};
}
#ifndef NDEBUG
static void dumpSpills(StringRef Title, const SpillInfo &Spills) {
dbgs() << "------------- " << Title << "--------------\n";
for (const auto &E : Spills) {
E.first->dump();
dbgs() << " user: ";
for (auto *I : E.second)
I->dump();
}
}
static void dumpAllocas(const SmallVectorImpl<AllocaInfo> &Allocas) {
dbgs() << "------------- Allocas --------------\n";
for (const auto &A : Allocas) {
A.Alloca->dump();
}
}
#endif
namespace {
using FieldIDType = size_t;
class FrameTypeBuilder {
private:
struct Field {
uint64_t Size;
uint64_t Offset;
Type *Ty;
FieldIDType LayoutFieldIndex;
Align Alignment;
Align TyAlignment;
uint64_t DynamicAlignBuffer;
};
const DataLayout &DL;
LLVMContext &Context;
uint64_t StructSize = 0;
Align StructAlign;
bool IsFinished = false;
Optional<Align> MaxFrameAlignment;
SmallVector<Field, 8> Fields;
DenseMap<Value*, unsigned> FieldIndexByKey;
public:
FrameTypeBuilder(LLVMContext &Context, const DataLayout &DL,
Optional<Align> MaxFrameAlignment)
: DL(DL), Context(Context), MaxFrameAlignment(MaxFrameAlignment) {}
LLVM_NODISCARD FieldIDType addFieldForAlloca(AllocaInst *AI,
bool IsHeader = false) {
Type *Ty = AI->getAllocatedType();
if (AI->isArrayAllocation()) {
if (auto *CI = dyn_cast<ConstantInt>(AI->getArraySize()))
Ty = ArrayType::get(Ty, CI->getValue().getZExtValue());
else
report_fatal_error("Coroutines cannot handle non static allocas yet");
}
return addField(Ty, AI->getAlign(), IsHeader);
}
void addFieldForAllocas(const Function &F, FrameDataInfo &FrameData,
coro::Shape &Shape);
LLVM_NODISCARD FieldIDType addField(Type *Ty, MaybeAlign MaybeFieldAlignment,
bool IsHeader = false,
bool IsSpillOfValue = false) {
assert(!IsFinished && "adding fields to a finished builder");
assert(Ty && "must provide a type for a field");
uint64_t FieldSize = DL.getTypeAllocSize(Ty);
if (FieldSize == 0) {
return 0;
}
Align ABIAlign = DL.getABITypeAlign(Ty);
Align TyAlignment = ABIAlign;
if (IsSpillOfValue && MaxFrameAlignment && *MaxFrameAlignment < ABIAlign)
TyAlignment = *MaxFrameAlignment;
Align FieldAlignment = MaybeFieldAlignment.value_or(TyAlignment);
uint64_t DynamicAlignBuffer = 0;
if (MaxFrameAlignment && (FieldAlignment > *MaxFrameAlignment)) {
DynamicAlignBuffer =
offsetToAlignment(MaxFrameAlignment->value(), FieldAlignment);
FieldAlignment = *MaxFrameAlignment;
FieldSize = FieldSize + DynamicAlignBuffer;
}
uint64_t Offset;
if (IsHeader) {
Offset = alignTo(StructSize, FieldAlignment);
StructSize = Offset + FieldSize;
} else {
Offset = OptimizedStructLayoutField::FlexibleOffset;
}
Fields.push_back({FieldSize, Offset, Ty, 0, FieldAlignment, TyAlignment,
DynamicAlignBuffer});
return Fields.size() - 1;
}
void finish(StructType *Ty);
uint64_t getStructSize() const {
assert(IsFinished && "not yet finished!");
return StructSize;
}
Align getStructAlign() const {
assert(IsFinished && "not yet finished!");
return StructAlign;
}
FieldIDType getLayoutFieldIndex(FieldIDType Id) const {
assert(IsFinished && "not yet finished!");
return Fields[Id].LayoutFieldIndex;
}
Field getLayoutField(FieldIDType Id) const {
assert(IsFinished && "not yet finished!");
return Fields[Id];
}
};
}
void FrameDataInfo::updateLayoutIndex(FrameTypeBuilder &B) {
auto Updater = [&](Value *I) {
auto Field = B.getLayoutField(getFieldIndex(I));
setFieldIndex(I, Field.LayoutFieldIndex);
setAlign(I, Field.Alignment);
uint64_t dynamicAlign =
Field.DynamicAlignBuffer
? Field.DynamicAlignBuffer + Field.Alignment.value()
: 0;
setDynamicAlign(I, dynamicAlign);
setOffset(I, Field.Offset);
};
LayoutIndexUpdateStarted = true;
for (auto &S : Spills)
Updater(S.first);
for (const auto &A : Allocas)
Updater(A.Alloca);
LayoutIndexUpdateStarted = false;
}
void FrameTypeBuilder::addFieldForAllocas(const Function &F,
FrameDataInfo &FrameData,
coro::Shape &Shape) {
using AllocaSetType = SmallVector<AllocaInst *, 4>;
SmallVector<AllocaSetType, 4> NonOverlapedAllocas;
auto AddFieldForAllocasAtExit = make_scope_exit([&]() {
for (auto AllocaList : NonOverlapedAllocas) {
auto *LargestAI = *AllocaList.begin();
FieldIDType Id = addFieldForAlloca(LargestAI);
for (auto *Alloca : AllocaList)
FrameData.setFieldIndex(Alloca, Id);
}
});
if (!Shape.OptimizeFrame) {
for (const auto &A : FrameData.Allocas) {
AllocaInst *Alloca = A.Alloca;
NonOverlapedAllocas.emplace_back(AllocaSetType(1, Alloca));
}
return;
}
DenseMap<SwitchInst *, BasicBlock *> DefaultSuspendDest;
for (auto CoroSuspendInst : Shape.CoroSuspends) {
for (auto U : CoroSuspendInst->users()) {
if (auto *ConstSWI = dyn_cast<SwitchInst>(U)) {
auto *SWI = const_cast<SwitchInst *>(ConstSWI);
DefaultSuspendDest[SWI] = SWI->getDefaultDest();
SWI->setDefaultDest(SWI->getSuccessor(1));
}
}
}
auto ExtractAllocas = [&]() {
AllocaSetType Allocas;
Allocas.reserve(FrameData.Allocas.size());
for (const auto &A : FrameData.Allocas)
Allocas.push_back(A.Alloca);
return Allocas;
};
StackLifetime StackLifetimeAnalyzer(F, ExtractAllocas(),
StackLifetime::LivenessType::May);
StackLifetimeAnalyzer.run();
auto IsAllocaInferenre = [&](const AllocaInst *AI1, const AllocaInst *AI2) {
return StackLifetimeAnalyzer.getLiveRange(AI1).overlaps(
StackLifetimeAnalyzer.getLiveRange(AI2));
};
auto GetAllocaSize = [&](const AllocaInfo &A) {
Optional<TypeSize> RetSize = A.Alloca->getAllocationSizeInBits(DL);
assert(RetSize && "Variable Length Arrays (VLA) are not supported.\n");
assert(!RetSize->isScalable() && "Scalable vectors are not yet supported");
return RetSize->getFixedSize();
};
sort(FrameData.Allocas, [&](const auto &Iter1, const auto &Iter2) {
return GetAllocaSize(Iter1) > GetAllocaSize(Iter2);
});
for (const auto &A : FrameData.Allocas) {
AllocaInst *Alloca = A.Alloca;
bool Merged = false;
for (auto &AllocaSet : NonOverlapedAllocas) {
assert(!AllocaSet.empty() && "Processing Alloca Set is not empty.\n");
bool NoInference = none_of(AllocaSet, [&](auto Iter) {
return IsAllocaInferenre(Alloca, Iter);
});
bool Alignable = [&]() -> bool {
auto *LargestAlloca = *AllocaSet.begin();
return LargestAlloca->getAlign().value() % Alloca->getAlign().value() ==
0;
}();
bool CouldMerge = NoInference && Alignable;
if (!CouldMerge)
continue;
AllocaSet.push_back(Alloca);
Merged = true;
break;
}
if (!Merged) {
NonOverlapedAllocas.emplace_back(AllocaSetType(1, Alloca));
}
}
for (auto SwitchAndDefaultDest : DefaultSuspendDest) {
SwitchInst *SWI = SwitchAndDefaultDest.first;
BasicBlock *DestBB = SwitchAndDefaultDest.second;
SWI->setDefaultDest(DestBB);
}
LLVM_DEBUG(for (auto &AllocaSet
: NonOverlapedAllocas) {
if (AllocaSet.size() > 1) {
dbgs() << "In Function:" << F.getName() << "\n";
dbgs() << "Find Union Set "
<< "\n";
dbgs() << "\tAllocas are \n";
for (auto Alloca : AllocaSet)
dbgs() << "\t\t" << *Alloca << "\n";
}
});
}
void FrameTypeBuilder::finish(StructType *Ty) {
assert(!IsFinished && "already finished!");
SmallVector<OptimizedStructLayoutField, 8> LayoutFields;
LayoutFields.reserve(Fields.size());
for (auto &Field : Fields) {
LayoutFields.emplace_back(&Field, Field.Size, Field.Alignment,
Field.Offset);
}
auto SizeAndAlign = performOptimizedStructLayout(LayoutFields);
StructSize = SizeAndAlign.first;
StructAlign = SizeAndAlign.second;
auto getField = [](const OptimizedStructLayoutField &LayoutField) -> Field & {
return *static_cast<Field *>(const_cast<void*>(LayoutField.Id));
};
bool Packed = [&] {
for (auto &LayoutField : LayoutFields) {
auto &F = getField(LayoutField);
if (!isAligned(F.TyAlignment, LayoutField.Offset))
return true;
}
return false;
}();
SmallVector<Type*, 16> FieldTypes;
FieldTypes.reserve(LayoutFields.size() * 3 / 2);
uint64_t LastOffset = 0;
for (auto &LayoutField : LayoutFields) {
auto &F = getField(LayoutField);
auto Offset = LayoutField.Offset;
assert(Offset >= LastOffset);
if (Offset != LastOffset) {
if (Packed || alignTo(LastOffset, F.TyAlignment) != Offset)
FieldTypes.push_back(ArrayType::get(Type::getInt8Ty(Context),
Offset - LastOffset));
}
F.Offset = Offset;
F.LayoutFieldIndex = FieldTypes.size();
FieldTypes.push_back(F.Ty);
if (F.DynamicAlignBuffer) {
FieldTypes.push_back(
ArrayType::get(Type::getInt8Ty(Context), F.DynamicAlignBuffer));
}
LastOffset = Offset + F.Size;
}
Ty->setBody(FieldTypes, Packed);
#ifndef NDEBUG
auto Layout = DL.getStructLayout(Ty);
for (auto &F : Fields) {
assert(Ty->getElementType(F.LayoutFieldIndex) == F.Ty);
assert(Layout->getElementOffset(F.LayoutFieldIndex) == F.Offset);
}
#endif
IsFinished = true;
}
static void cacheDIVar(FrameDataInfo &FrameData,
DenseMap<Value *, DILocalVariable *> &DIVarCache) {
for (auto *V : FrameData.getAllDefs()) {
if (DIVarCache.find(V) != DIVarCache.end())
continue;
auto DDIs = FindDbgDeclareUses(V);
auto *I = llvm::find_if(DDIs, [](DbgDeclareInst *DDI) {
return DDI->getExpression()->getNumElements() == 0;
});
if (I != DDIs.end())
DIVarCache.insert({V, (*I)->getVariable()});
}
}
static StringRef solveTypeName(Type *Ty) {
if (Ty->isIntegerTy()) {
SmallString<16> Buffer;
raw_svector_ostream OS(Buffer);
OS << "__int_" << cast<IntegerType>(Ty)->getBitWidth();
auto *MDName = MDString::get(Ty->getContext(), OS.str());
return MDName->getString();
}
if (Ty->isFloatingPointTy()) {
if (Ty->isFloatTy())
return "__float_";
if (Ty->isDoubleTy())
return "__double_";
return "__floating_type_";
}
if (auto *PtrTy = dyn_cast<PointerType>(Ty)) {
if (PtrTy->isOpaque())
return "PointerType";
Type *PointeeTy = PtrTy->getNonOpaquePointerElementType();
auto Name = solveTypeName(PointeeTy);
if (Name == "UnknownType")
return "PointerType";
SmallString<16> Buffer;
Twine(Name + "_Ptr").toStringRef(Buffer);
auto *MDName = MDString::get(Ty->getContext(), Buffer.str());
return MDName->getString();
}
if (Ty->isStructTy()) {
if (!cast<StructType>(Ty)->hasName())
return "__LiteralStructType_";
auto Name = Ty->getStructName();
SmallString<16> Buffer(Name);
for (auto &Iter : Buffer)
if (Iter == '.' || Iter == ':')
Iter = '_';
auto *MDName = MDString::get(Ty->getContext(), Buffer.str());
return MDName->getString();
}
return "UnknownType";
}
static DIType *solveDIType(DIBuilder &Builder, Type *Ty,
const DataLayout &Layout, DIScope *Scope,
unsigned LineNum,
DenseMap<Type *, DIType *> &DITypeCache) {
if (DIType *DT = DITypeCache.lookup(Ty))
return DT;
StringRef Name = solveTypeName(Ty);
DIType *RetType = nullptr;
if (Ty->isIntegerTy()) {
auto BitWidth = cast<IntegerType>(Ty)->getBitWidth();
RetType = Builder.createBasicType(Name, BitWidth, dwarf::DW_ATE_signed,
llvm::DINode::FlagArtificial);
} else if (Ty->isFloatingPointTy()) {
RetType = Builder.createBasicType(Name, Layout.getTypeSizeInBits(Ty),
dwarf::DW_ATE_float,
llvm::DINode::FlagArtificial);
} else if (Ty->isPointerTy()) {
RetType = Builder.createPointerType(nullptr, Layout.getTypeSizeInBits(Ty),
Layout.getABITypeAlignment(Ty),
None, Name);
} else if (Ty->isStructTy()) {
auto *DIStruct = Builder.createStructType(
Scope, Name, Scope->getFile(), LineNum, Layout.getTypeSizeInBits(Ty),
Layout.getPrefTypeAlignment(Ty), llvm::DINode::FlagArtificial, nullptr,
llvm::DINodeArray());
auto *StructTy = cast<StructType>(Ty);
SmallVector<Metadata *, 16> Elements;
for (unsigned I = 0; I < StructTy->getNumElements(); I++) {
DIType *DITy = solveDIType(Builder, StructTy->getElementType(I), Layout,
Scope, LineNum, DITypeCache);
assert(DITy);
Elements.push_back(Builder.createMemberType(
Scope, DITy->getName(), Scope->getFile(), LineNum,
DITy->getSizeInBits(), DITy->getAlignInBits(),
Layout.getStructLayout(StructTy)->getElementOffsetInBits(I),
llvm::DINode::FlagArtificial, DITy));
}
Builder.replaceArrays(DIStruct, Builder.getOrCreateArray(Elements));
RetType = DIStruct;
} else {
LLVM_DEBUG(dbgs() << "Unresolved Type: " << *Ty << "\n");
TypeSize Size = Layout.getTypeSizeInBits(Ty);
auto *CharSizeType = Builder.createBasicType(
Name, 8, dwarf::DW_ATE_unsigned_char, llvm::DINode::FlagArtificial);
if (Size <= 8)
RetType = CharSizeType;
else {
if (Size % 8 != 0)
Size = TypeSize::Fixed(Size + 8 - (Size % 8));
RetType = Builder.createArrayType(
Size, Layout.getPrefTypeAlign(Ty).value(), CharSizeType,
Builder.getOrCreateArray(Builder.getOrCreateSubrange(0, Size / 8)));
}
}
DITypeCache.insert({Ty, RetType});
return RetType;
}
static void buildFrameDebugInfo(Function &F, coro::Shape &Shape,
FrameDataInfo &FrameData) {
DISubprogram *DIS = F.getSubprogram();
if (!DIS || !DIS->getUnit() ||
!dwarf::isCPlusPlus(
(dwarf::SourceLanguage)DIS->getUnit()->getSourceLanguage()))
return;
assert(Shape.ABI == coro::ABI::Switch &&
"We could only build debug infomation for C++ coroutine now.\n");
DIBuilder DBuilder(*F.getParent(), false);
AllocaInst *PromiseAlloca = Shape.getPromiseAlloca();
assert(PromiseAlloca &&
"Coroutine with switch ABI should own Promise alloca");
TinyPtrVector<DbgDeclareInst *> DIs = FindDbgDeclareUses(PromiseAlloca);
if (DIs.empty())
return;
DbgDeclareInst *PromiseDDI = DIs.front();
DILocalVariable *PromiseDIVariable = PromiseDDI->getVariable();
DILocalScope *PromiseDIScope = PromiseDIVariable->getScope();
DIFile *DFile = PromiseDIScope->getFile();
DILocation *DILoc = PromiseDDI->getDebugLoc().get();
unsigned LineNum = PromiseDIVariable->getLine();
DICompositeType *FrameDITy = DBuilder.createStructType(
DIS->getUnit(), Twine(F.getName() + ".coro_frame_ty").str(),
DFile, LineNum, Shape.FrameSize * 8,
Shape.FrameAlign.value() * 8, llvm::DINode::FlagArtificial, nullptr,
llvm::DINodeArray());
StructType *FrameTy = Shape.FrameTy;
SmallVector<Metadata *, 16> Elements;
DataLayout Layout = F.getParent()->getDataLayout();
DenseMap<Value *, DILocalVariable *> DIVarCache;
cacheDIVar(FrameData, DIVarCache);
unsigned ResumeIndex = coro::Shape::SwitchFieldIndex::Resume;
unsigned DestroyIndex = coro::Shape::SwitchFieldIndex::Destroy;
unsigned IndexIndex = Shape.SwitchLowering.IndexField;
DenseMap<unsigned, StringRef> NameCache;
NameCache.insert({ResumeIndex, "__resume_fn"});
NameCache.insert({DestroyIndex, "__destroy_fn"});
NameCache.insert({IndexIndex, "__coro_index"});
Type *ResumeFnTy = FrameTy->getElementType(ResumeIndex),
*DestroyFnTy = FrameTy->getElementType(DestroyIndex),
*IndexTy = FrameTy->getElementType(IndexIndex);
DenseMap<unsigned, DIType *> TyCache;
TyCache.insert(
{ResumeIndex, DBuilder.createPointerType(
nullptr, Layout.getTypeSizeInBits(ResumeFnTy))});
TyCache.insert(
{DestroyIndex, DBuilder.createPointerType(
nullptr, Layout.getTypeSizeInBits(DestroyFnTy))});
TyCache.insert({IndexIndex, DBuilder.createBasicType(
"__coro_index",
(Layout.getTypeSizeInBits(IndexTy) < 8)
? 8
: Layout.getTypeSizeInBits(IndexTy),
dwarf::DW_ATE_unsigned_char)});
for (auto *V : FrameData.getAllDefs()) {
if (DIVarCache.find(V) == DIVarCache.end())
continue;
auto Index = FrameData.getFieldIndex(V);
NameCache.insert({Index, DIVarCache[V]->getName()});
TyCache.insert({Index, DIVarCache[V]->getType()});
}
DenseMap<unsigned, std::pair<unsigned, unsigned>> OffsetCache;
OffsetCache.insert({ResumeIndex, {8, 0}});
OffsetCache.insert({DestroyIndex, {8, 8}});
OffsetCache.insert(
{IndexIndex,
{Shape.SwitchLowering.IndexAlign, Shape.SwitchLowering.IndexOffset}});
for (auto *V : FrameData.getAllDefs()) {
auto Index = FrameData.getFieldIndex(V);
OffsetCache.insert(
{Index, {FrameData.getAlign(V).value(), FrameData.getOffset(V)}});
}
DenseMap<Type *, DIType *> DITypeCache;
unsigned UnknownTypeNum = 0;
for (unsigned Index = 0; Index < FrameTy->getNumElements(); Index++) {
if (OffsetCache.find(Index) == OffsetCache.end())
continue;
std::string Name;
uint64_t SizeInBits;
uint32_t AlignInBits;
uint64_t OffsetInBits;
DIType *DITy = nullptr;
Type *Ty = FrameTy->getElementType(Index);
assert(Ty->isSized() && "We can't handle type which is not sized.\n");
SizeInBits = Layout.getTypeSizeInBits(Ty).getFixedSize();
AlignInBits = OffsetCache[Index].first * 8;
OffsetInBits = OffsetCache[Index].second * 8;
if (NameCache.find(Index) != NameCache.end()) {
Name = NameCache[Index].str();
DITy = TyCache[Index];
} else {
DITy = solveDIType(DBuilder, Ty, Layout, FrameDITy, LineNum, DITypeCache);
assert(DITy && "SolveDIType shouldn't return nullptr.\n");
Name = DITy->getName().str();
Name += "_" + std::to_string(UnknownTypeNum);
UnknownTypeNum++;
}
Elements.push_back(DBuilder.createMemberType(
FrameDITy, Name, DFile, LineNum, SizeInBits, AlignInBits, OffsetInBits,
llvm::DINode::FlagArtificial, DITy));
}
DBuilder.replaceArrays(FrameDITy, DBuilder.getOrCreateArray(Elements));
auto *FrameDIVar = DBuilder.createAutoVariable(PromiseDIScope, "__coro_frame",
DFile, LineNum, FrameDITy,
true, DINode::FlagArtificial);
assert(FrameDIVar->isValidLocationForIntrinsic(PromiseDDI->getDebugLoc()));
if (auto *SubProgram = dyn_cast<DISubprogram>(PromiseDIScope)) {
auto RetainedNodes = SubProgram->getRetainedNodes();
SmallVector<Metadata *, 32> RetainedNodesVec(RetainedNodes.begin(),
RetainedNodes.end());
RetainedNodesVec.push_back(FrameDIVar);
SubProgram->replaceOperandWith(
7, (MDTuple::get(F.getContext(), RetainedNodesVec)));
}
DBuilder.insertDeclare(Shape.FramePtr, FrameDIVar,
DBuilder.createExpression(), DILoc,
Shape.getInsertPtAfterFramePtr());
}
static StructType *buildFrameType(Function &F, coro::Shape &Shape,
FrameDataInfo &FrameData) {
LLVMContext &C = F.getContext();
const DataLayout &DL = F.getParent()->getDataLayout();
StructType *FrameTy = [&] {
SmallString<32> Name(F.getName());
Name.append(".Frame");
return StructType::create(C, Name);
}();
Optional<Align> MaxFrameAlignment;
if (Shape.ABI == coro::ABI::Async)
MaxFrameAlignment = Shape.AsyncLowering.getContextAlignment();
FrameTypeBuilder B(C, DL, MaxFrameAlignment);
AllocaInst *PromiseAlloca = Shape.getPromiseAlloca();
Optional<FieldIDType> SwitchIndexFieldId;
if (Shape.ABI == coro::ABI::Switch) {
auto *FramePtrTy = FrameTy->getPointerTo();
auto *FnTy = FunctionType::get(Type::getVoidTy(C), FramePtrTy,
false);
auto *FnPtrTy = FnTy->getPointerTo();
(void)B.addField(FnPtrTy, None, true);
(void)B.addField(FnPtrTy, None, true);
if (PromiseAlloca)
FrameData.setFieldIndex(
PromiseAlloca, B.addFieldForAlloca(PromiseAlloca, true));
unsigned IndexBits = std::max(1U, Log2_64_Ceil(Shape.CoroSuspends.size()));
Type *IndexType = Type::getIntNTy(C, IndexBits);
SwitchIndexFieldId = B.addField(IndexType, None);
} else {
assert(PromiseAlloca == nullptr && "lowering doesn't support promises");
}
B.addFieldForAllocas(F, FrameData, Shape);
if (Shape.ABI == coro::ABI::Switch && PromiseAlloca)
FrameData.Allocas.emplace_back(
PromiseAlloca, DenseMap<Instruction *, llvm::Optional<APInt>>{}, false);
for (auto &S : FrameData.Spills) {
Type *FieldType = S.first->getType();
if (const Argument *A = dyn_cast<Argument>(S.first))
if (A->hasByValAttr())
FieldType = A->getParamByValType();
FieldIDType Id =
B.addField(FieldType, None, false , true );
FrameData.setFieldIndex(S.first, Id);
}
B.finish(FrameTy);
FrameData.updateLayoutIndex(B);
Shape.FrameAlign = B.getStructAlign();
Shape.FrameSize = B.getStructSize();
switch (Shape.ABI) {
case coro::ABI::Switch: {
auto IndexField = B.getLayoutField(*SwitchIndexFieldId);
Shape.SwitchLowering.IndexField = IndexField.LayoutFieldIndex;
Shape.SwitchLowering.IndexAlign = IndexField.Alignment.value();
Shape.SwitchLowering.IndexOffset = IndexField.Offset;
Shape.FrameSize = alignTo(Shape.FrameSize, Shape.FrameAlign);
break;
}
case coro::ABI::Retcon:
case coro::ABI::RetconOnce: {
auto Id = Shape.getRetconCoroId();
Shape.RetconLowering.IsFrameInlineInStorage
= (B.getStructSize() <= Id->getStorageSize() &&
B.getStructAlign() <= Id->getStorageAlignment());
break;
}
case coro::ABI::Async: {
Shape.AsyncLowering.FrameOffset =
alignTo(Shape.AsyncLowering.ContextHeaderSize, Shape.FrameAlign);
Shape.AsyncLowering.ContextSize =
alignTo(Shape.AsyncLowering.FrameOffset + Shape.FrameSize,
Shape.AsyncLowering.getContextAlignment());
if (Shape.AsyncLowering.getContextAlignment() < Shape.FrameAlign) {
report_fatal_error(
"The alignment requirment of frame variables cannot be higher than "
"the alignment of the async function context");
}
break;
}
}
return FrameTy;
}
namespace {
struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> {
using Base = PtrUseVisitor<AllocaUseVisitor>;
AllocaUseVisitor(const DataLayout &DL, const DominatorTree &DT,
const CoroBeginInst &CB, const SuspendCrossingInfo &Checker,
bool ShouldUseLifetimeStartInfo)
: PtrUseVisitor(DL), DT(DT), CoroBegin(CB), Checker(Checker),
ShouldUseLifetimeStartInfo(ShouldUseLifetimeStartInfo) {}
void visit(Instruction &I) {
Users.insert(&I);
Base::visit(I);
if (PI.isEscaped() && !DT.dominates(&CoroBegin, PI.getEscapingInst())) {
MayWriteBeforeCoroBegin = true;
}
}
void visit(Instruction *I) { return visit(*I); }
void visitPHINode(PHINode &I) {
enqueueUsers(I);
handleAlias(I);
}
void visitSelectInst(SelectInst &I) {
enqueueUsers(I);
handleAlias(I);
}
void visitStoreInst(StoreInst &SI) {
handleMayWrite(SI);
if (SI.getValueOperand() != U->get())
return;
auto IsSimpleStoreThenLoad = [&]() {
auto *AI = dyn_cast<AllocaInst>(SI.getPointerOperand());
if (!AI)
return false;
SmallVector<Instruction *, 4> StoreAliases = {AI};
while (!StoreAliases.empty()) {
Instruction *I = StoreAliases.pop_back_val();
for (User *U : I->users()) {
if (auto *LI = dyn_cast<LoadInst>(U)) {
enqueueUsers(*LI);
handleAlias(*LI);
continue;
}
if (auto *S = dyn_cast<StoreInst>(U))
if (S->getPointerOperand() == I)
continue;
if (auto *II = dyn_cast<IntrinsicInst>(U))
if (II->isLifetimeStartOrEnd())
continue;
if (auto *BI = dyn_cast<BitCastInst>(U)) {
StoreAliases.push_back(BI);
continue;
}
return false;
}
}
return true;
};
if (!IsSimpleStoreThenLoad())
PI.setEscaped(&SI);
}
void visitMemIntrinsic(MemIntrinsic &MI) { handleMayWrite(MI); }
void visitBitCastInst(BitCastInst &BC) {
Base::visitBitCastInst(BC);
handleAlias(BC);
}
void visitAddrSpaceCastInst(AddrSpaceCastInst &ASC) {
Base::visitAddrSpaceCastInst(ASC);
handleAlias(ASC);
}
void visitGetElementPtrInst(GetElementPtrInst &GEPI) {
Base::visitGetElementPtrInst(GEPI);
handleAlias(GEPI);
}
void visitIntrinsicInst(IntrinsicInst &II) {
if (II.getIntrinsicID() != Intrinsic::lifetime_start || !IsOffsetKnown ||
!Offset.isZero())
return Base::visitIntrinsicInst(II);
LifetimeStarts.insert(&II);
}
void visitCallBase(CallBase &CB) {
for (unsigned Op = 0, OpCount = CB.arg_size(); Op < OpCount; ++Op)
if (U->get() == CB.getArgOperand(Op) && !CB.doesNotCapture(Op))
PI.setEscaped(&CB);
handleMayWrite(CB);
}
bool getShouldLiveOnFrame() const {
if (!ShouldLiveOnFrame)
ShouldLiveOnFrame = computeShouldLiveOnFrame();
return *ShouldLiveOnFrame;
}
bool getMayWriteBeforeCoroBegin() const { return MayWriteBeforeCoroBegin; }
DenseMap<Instruction *, llvm::Optional<APInt>> getAliasesCopy() const {
assert(getShouldLiveOnFrame() && "This method should only be called if the "
"alloca needs to live on the frame.");
for (const auto &P : AliasOffetMap)
if (!P.second)
report_fatal_error("Unable to handle an alias with unknown offset "
"created before CoroBegin.");
return AliasOffetMap;
}
private:
const DominatorTree &DT;
const CoroBeginInst &CoroBegin;
const SuspendCrossingInfo &Checker;
DenseMap<Instruction *, llvm::Optional<APInt>> AliasOffetMap{};
SmallPtrSet<Instruction *, 4> Users{};
SmallPtrSet<IntrinsicInst *, 2> LifetimeStarts{};
bool MayWriteBeforeCoroBegin{false};
bool ShouldUseLifetimeStartInfo{true};
mutable llvm::Optional<bool> ShouldLiveOnFrame{};
bool computeShouldLiveOnFrame() const {
if (ShouldUseLifetimeStartInfo && !LifetimeStarts.empty()) {
for (auto *I : Users)
for (auto *S : LifetimeStarts)
if (Checker.isDefinitionAcrossSuspend(*S, I))
return true;
return false;
}
if (PI.isEscaped())
return true;
for (auto *U1 : Users)
for (auto *U2 : Users)
if (Checker.isDefinitionAcrossSuspend(*U1, U2))
return true;
return false;
}
void handleMayWrite(const Instruction &I) {
if (!DT.dominates(&CoroBegin, &I))
MayWriteBeforeCoroBegin = true;
}
bool usedAfterCoroBegin(Instruction &I) {
for (auto &U : I.uses())
if (DT.dominates(&CoroBegin, U))
return true;
return false;
}
void handleAlias(Instruction &I) {
if (DT.dominates(&CoroBegin, &I) || !usedAfterCoroBegin(I))
return;
if (!IsOffsetKnown) {
AliasOffetMap[&I].reset();
} else {
auto Itr = AliasOffetMap.find(&I);
if (Itr == AliasOffetMap.end()) {
AliasOffetMap[&I] = Offset;
} else if (Itr->second && *Itr->second != Offset) {
AliasOffetMap[&I].reset();
}
}
}
};
}
static Instruction *splitBeforeCatchSwitch(CatchSwitchInst *CatchSwitch) {
BasicBlock *CurrentBlock = CatchSwitch->getParent();
BasicBlock *NewBlock = CurrentBlock->splitBasicBlock(CatchSwitch);
CurrentBlock->getTerminator()->eraseFromParent();
auto *CleanupPad =
CleanupPadInst::Create(CatchSwitch->getParentPad(), {}, "", CurrentBlock);
auto *CleanupRet =
CleanupReturnInst::Create(CleanupPad, NewBlock, CurrentBlock);
return CleanupRet;
}
static void createFramePtr(coro::Shape &Shape) {
auto *CB = Shape.CoroBegin;
IRBuilder<> Builder(CB->getNextNode());
StructType *FrameTy = Shape.FrameTy;
PointerType *FramePtrTy = FrameTy->getPointerTo();
Shape.FramePtr =
cast<Instruction>(Builder.CreateBitCast(CB, FramePtrTy, "FramePtr"));
}
static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
auto *CB = Shape.CoroBegin;
LLVMContext &C = CB->getContext();
IRBuilder<> Builder(C);
StructType *FrameTy = Shape.FrameTy;
Value *FramePtr = Shape.FramePtr;
DominatorTree DT(*CB->getFunction());
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> DbgPtrAllocaCache;
auto GetFramePointer = [&](Value *Orig) -> Value * {
FieldIDType Index = FrameData.getFieldIndex(Orig);
SmallVector<Value *, 3> Indices = {
ConstantInt::get(Type::getInt32Ty(C), 0),
ConstantInt::get(Type::getInt32Ty(C), Index),
};
if (auto *AI = dyn_cast<AllocaInst>(Orig)) {
if (auto *CI = dyn_cast<ConstantInt>(AI->getArraySize())) {
auto Count = CI->getValue().getZExtValue();
if (Count > 1) {
Indices.push_back(ConstantInt::get(Type::getInt32Ty(C), 0));
}
} else {
report_fatal_error("Coroutines cannot handle non static allocas yet");
}
}
auto GEP = cast<GetElementPtrInst>(
Builder.CreateInBoundsGEP(FrameTy, FramePtr, Indices));
if (auto *AI = dyn_cast<AllocaInst>(Orig)) {
if (FrameData.getDynamicAlign(Orig) != 0) {
assert(FrameData.getDynamicAlign(Orig) == AI->getAlign().value());
auto *M = AI->getModule();
auto *IntPtrTy = M->getDataLayout().getIntPtrType(AI->getType());
auto *PtrValue = Builder.CreatePtrToInt(GEP, IntPtrTy);
auto *AlignMask =
ConstantInt::get(IntPtrTy, AI->getAlign().value() - 1);
PtrValue = Builder.CreateAdd(PtrValue, AlignMask);
PtrValue = Builder.CreateAnd(PtrValue, Builder.CreateNot(AlignMask));
return Builder.CreateIntToPtr(PtrValue, AI->getType());
}
if (GEP->getResultElementType() != Orig->getType())
return Builder.CreateBitCast(GEP, Orig->getType(),
Orig->getName() + Twine(".cast"));
}
return GEP;
};
for (auto const &E : FrameData.Spills) {
Value *Def = E.first;
auto SpillAlignment = Align(FrameData.getAlign(Def));
Instruction *InsertPt = nullptr;
Type *ByValTy = nullptr;
if (auto *Arg = dyn_cast<Argument>(Def)) {
InsertPt = Shape.getInsertPtAfterFramePtr();
Arg->getParent()->removeParamAttr(Arg->getArgNo(), Attribute::NoCapture);
if (Arg->hasByValAttr())
ByValTy = Arg->getParamByValType();
} else if (auto *CSI = dyn_cast<AnyCoroSuspendInst>(Def)) {
InsertPt = CSI->getParent()->getSingleSuccessor()->getFirstNonPHI();
} else {
auto *I = cast<Instruction>(Def);
if (!DT.dominates(CB, I)) {
InsertPt = Shape.getInsertPtAfterFramePtr();
} else if (auto *II = dyn_cast<InvokeInst>(I)) {
auto *NewBB = SplitEdge(II->getParent(), II->getNormalDest());
InsertPt = NewBB->getTerminator();
} else if (isa<PHINode>(I)) {
BasicBlock *DefBlock = I->getParent();
if (auto *CSI = dyn_cast<CatchSwitchInst>(DefBlock->getTerminator()))
InsertPt = splitBeforeCatchSwitch(CSI);
else
InsertPt = &*DefBlock->getFirstInsertionPt();
} else {
assert(!I->isTerminator() && "unexpected terminator");
InsertPt = I->getNextNode();
}
}
auto Index = FrameData.getFieldIndex(Def);
Builder.SetInsertPoint(InsertPt);
auto *G = Builder.CreateConstInBoundsGEP2_32(
FrameTy, FramePtr, 0, Index, Def->getName() + Twine(".spill.addr"));
if (ByValTy) {
auto *Value = Builder.CreateLoad(ByValTy, Def);
Builder.CreateAlignedStore(Value, G, SpillAlignment);
} else {
Builder.CreateAlignedStore(Def, G, SpillAlignment);
}
BasicBlock *CurrentBlock = nullptr;
Value *CurrentReload = nullptr;
for (auto *U : E.second) {
if (CurrentBlock != U->getParent()) {
CurrentBlock = U->getParent();
Builder.SetInsertPoint(&*CurrentBlock->getFirstInsertionPt());
auto *GEP = GetFramePointer(E.first);
GEP->setName(E.first->getName() + Twine(".reload.addr"));
if (ByValTy)
CurrentReload = GEP;
else
CurrentReload = Builder.CreateAlignedLoad(
FrameTy->getElementType(FrameData.getFieldIndex(E.first)), GEP,
SpillAlignment, E.first->getName() + Twine(".reload"));
TinyPtrVector<DbgDeclareInst *> DIs = FindDbgDeclareUses(Def);
for (DbgDeclareInst *DDI : DIs) {
bool AllowUnresolved = false;
DIBuilder(*CurrentBlock->getParent()->getParent(), AllowUnresolved)
.insertDeclare(CurrentReload, DDI->getVariable(),
DDI->getExpression(), DDI->getDebugLoc(),
&*Builder.GetInsertPoint());
coro::salvageDebugInfo(DbgPtrAllocaCache, DDI, Shape.OptimizeFrame);
}
}
if (auto *DI = dyn_cast<DbgAddrIntrinsic>(U)) {
coro::salvageDebugInfo(DbgPtrAllocaCache, DI, Shape.OptimizeFrame);
}
if (auto *PN = dyn_cast<PHINode>(U)) {
assert(PN->getNumIncomingValues() == 1 &&
"unexpected number of incoming "
"values in the PHINode");
PN->replaceAllUsesWith(CurrentReload);
PN->eraseFromParent();
continue;
}
U->replaceUsesOfWith(Def, CurrentReload);
}
}
BasicBlock *FramePtrBB = Shape.getInsertPtAfterFramePtr()->getParent();
auto SpillBlock = FramePtrBB->splitBasicBlock(
Shape.getInsertPtAfterFramePtr(), "AllocaSpillBB");
SpillBlock->splitBasicBlock(&SpillBlock->front(), "PostSpill");
Shape.AllocaSpillBlock = SpillBlock;
if (Shape.ABI == coro::ABI::Retcon || Shape.ABI == coro::ABI::RetconOnce ||
Shape.ABI == coro::ABI::Async) {
Builder.SetInsertPoint(&SpillBlock->front());
for (const auto &P : FrameData.Allocas) {
AllocaInst *Alloca = P.Alloca;
auto *G = GetFramePointer(Alloca);
G->takeName(Alloca);
Alloca->replaceAllUsesWith(G);
Alloca->eraseFromParent();
}
return;
}
Builder.SetInsertPoint(&Shape.AllocaSpillBlock->front());
SmallVector<Instruction *, 4> UsersToUpdate;
for (const auto &A : FrameData.Allocas) {
AllocaInst *Alloca = A.Alloca;
UsersToUpdate.clear();
for (User *U : Alloca->users()) {
auto *I = cast<Instruction>(U);
if (DT.dominates(CB, I))
UsersToUpdate.push_back(I);
}
if (UsersToUpdate.empty())
continue;
auto *G = GetFramePointer(Alloca);
G->setName(Alloca->getName() + Twine(".reload.addr"));
SmallVector<DbgVariableIntrinsic *, 4> DIs;
findDbgUsers(DIs, Alloca);
for (auto *DVI : DIs)
DVI->replaceUsesOfWith(Alloca, G);
for (Instruction *I : UsersToUpdate)
I->replaceUsesOfWith(Alloca, G);
}
Builder.SetInsertPoint(Shape.getInsertPtAfterFramePtr());
for (const auto &A : FrameData.Allocas) {
AllocaInst *Alloca = A.Alloca;
if (A.MayWriteBeforeCoroBegin) {
if (Alloca->isArrayAllocation())
report_fatal_error(
"Coroutines cannot handle copying of array allocas yet");
auto *G = GetFramePointer(Alloca);
auto *Value = Builder.CreateLoad(Alloca->getAllocatedType(), Alloca);
Builder.CreateStore(Value, G);
}
for (const auto &Alias : A.Aliases) {
auto *FramePtr = GetFramePointer(Alloca);
auto *FramePtrRaw =
Builder.CreateBitCast(FramePtr, Type::getInt8PtrTy(C));
auto &Value = *Alias.second;
auto ITy = IntegerType::get(C, Value.getBitWidth());
auto *AliasPtr = Builder.CreateGEP(Type::getInt8Ty(C), FramePtrRaw,
ConstantInt::get(ITy, Value));
auto *AliasPtrTyped =
Builder.CreateBitCast(AliasPtr, Alias.first->getType());
Alias.first->replaceUsesWithIf(
AliasPtrTyped, [&](Use &U) { return DT.dominates(CB, U); });
}
}
}
static void movePHIValuesToInsertedBlock(BasicBlock *SuccBB,
BasicBlock *InsertedBB,
BasicBlock *PredBB,
PHINode *UntilPHI = nullptr) {
auto *PN = cast<PHINode>(&SuccBB->front());
do {
int Index = PN->getBasicBlockIndex(InsertedBB);
Value *V = PN->getIncomingValue(Index);
PHINode *InputV = PHINode::Create(
V->getType(), 1, V->getName() + Twine(".") + SuccBB->getName(),
&InsertedBB->front());
InputV->addIncoming(V, PredBB);
PN->setIncomingValue(Index, InputV);
PN = dyn_cast<PHINode>(PN->getNextNode());
} while (PN != UntilPHI);
}
static void rewritePHIsForCleanupPad(BasicBlock *CleanupPadBB,
CleanupPadInst *CleanupPad) {
auto *UnreachBB = BasicBlock::Create(
CleanupPadBB->getContext(), "unreachable", CleanupPadBB->getParent());
IRBuilder<> Builder(UnreachBB);
Builder.CreateUnreachable();
auto *NewCleanupPadBB =
BasicBlock::Create(CleanupPadBB->getContext(),
CleanupPadBB->getName() + Twine(".corodispatch"),
CleanupPadBB->getParent(), CleanupPadBB);
Builder.SetInsertPoint(NewCleanupPadBB);
auto *SwitchType = Builder.getInt8Ty();
auto *SetDispatchValuePN =
Builder.CreatePHI(SwitchType, pred_size(CleanupPadBB));
CleanupPad->removeFromParent();
CleanupPad->insertAfter(SetDispatchValuePN);
auto *SwitchOnDispatch = Builder.CreateSwitch(SetDispatchValuePN, UnreachBB,
pred_size(CleanupPadBB));
int SwitchIndex = 0;
SmallVector<BasicBlock *, 8> Preds(predecessors(CleanupPadBB));
for (BasicBlock *Pred : Preds) {
auto *CaseBB = BasicBlock::Create(CleanupPadBB->getContext(),
CleanupPadBB->getName() +
Twine(".from.") + Pred->getName(),
CleanupPadBB->getParent(), CleanupPadBB);
updatePhiNodes(CleanupPadBB, Pred, CaseBB);
CaseBB->setName(CleanupPadBB->getName() + Twine(".from.") +
Pred->getName());
Builder.SetInsertPoint(CaseBB);
Builder.CreateBr(CleanupPadBB);
movePHIValuesToInsertedBlock(CleanupPadBB, CaseBB, NewCleanupPadBB);
setUnwindEdgeTo(Pred->getTerminator(), NewCleanupPadBB);
auto *SwitchConstant = ConstantInt::get(SwitchType, SwitchIndex);
SetDispatchValuePN->addIncoming(SwitchConstant, Pred);
SwitchOnDispatch->addCase(SwitchConstant, CaseBB);
SwitchIndex++;
}
}
static void cleanupSinglePredPHIs(Function &F) {
SmallVector<PHINode *, 32> Worklist;
for (auto &BB : F) {
for (auto &Phi : BB.phis()) {
if (Phi.getNumIncomingValues() == 1) {
Worklist.push_back(&Phi);
} else
break;
}
}
while (!Worklist.empty()) {
auto *Phi = Worklist.pop_back_val();
auto *OriginalValue = Phi->getIncomingValue(0);
Phi->replaceAllUsesWith(OriginalValue);
}
}
static void rewritePHIs(BasicBlock &BB) {
if (auto *CleanupPad =
dyn_cast_or_null<CleanupPadInst>(BB.getFirstNonPHI())) {
SmallVector<BasicBlock *, 8> Preds(predecessors(&BB));
for (BasicBlock *Pred : Preds) {
if (CatchSwitchInst *CS =
dyn_cast<CatchSwitchInst>(Pred->getTerminator())) {
assert(CS->getUnwindDest() == &BB);
(void)CS;
rewritePHIsForCleanupPad(&BB, CleanupPad);
return;
}
}
}
LandingPadInst *LandingPad = nullptr;
PHINode *ReplPHI = nullptr;
if ((LandingPad = dyn_cast_or_null<LandingPadInst>(BB.getFirstNonPHI()))) {
ReplPHI = PHINode::Create(LandingPad->getType(), 1, "", LandingPad);
ReplPHI->takeName(LandingPad);
LandingPad->replaceAllUsesWith(ReplPHI);
}
SmallVector<BasicBlock *, 8> Preds(predecessors(&BB));
for (BasicBlock *Pred : Preds) {
auto *IncomingBB = ehAwareSplitEdge(Pred, &BB, LandingPad, ReplPHI);
IncomingBB->setName(BB.getName() + Twine(".from.") + Pred->getName());
movePHIValuesToInsertedBlock(&BB, IncomingBB, Pred, ReplPHI);
}
if (LandingPad) {
LandingPad->eraseFromParent();
}
}
static void rewritePHIs(Function &F) {
SmallVector<BasicBlock *, 8> WorkList;
for (BasicBlock &BB : F)
if (auto *PN = dyn_cast<PHINode>(&BB.front()))
if (PN->getNumIncomingValues() > 1)
WorkList.push_back(&BB);
for (BasicBlock *BB : WorkList)
rewritePHIs(*BB);
}
static bool materializable(Instruction &V) {
return isa<CastInst>(&V) || isa<GetElementPtrInst>(&V) ||
isa<BinaryOperator>(&V) || isa<CmpInst>(&V) || isa<SelectInst>(&V);
}
static bool isCoroutineStructureIntrinsic(Instruction &I) {
return isa<CoroIdInst>(&I) || isa<CoroSaveInst>(&I) ||
isa<CoroSuspendInst>(&I);
}
static void rewriteMaterializableInstructions(IRBuilder<> &IRB,
const SpillInfo &Spills) {
for (const auto &E : Spills) {
Value *Def = E.first;
BasicBlock *CurrentBlock = nullptr;
Instruction *CurrentMaterialization = nullptr;
for (Instruction *U : E.second) {
if (CurrentBlock != U->getParent()) {
bool IsInCoroSuspendBlock = isa<AnyCoroSuspendInst>(U);
CurrentBlock = U->getParent();
auto *InsertBlock = IsInCoroSuspendBlock
? CurrentBlock->getSinglePredecessor()
: CurrentBlock;
CurrentMaterialization = cast<Instruction>(Def)->clone();
CurrentMaterialization->setName(Def->getName());
CurrentMaterialization->insertBefore(
IsInCoroSuspendBlock ? InsertBlock->getTerminator()
: &*InsertBlock->getFirstInsertionPt());
}
if (auto *PN = dyn_cast<PHINode>(U)) {
assert(PN->getNumIncomingValues() == 1 &&
"unexpected number of incoming "
"values in the PHINode");
PN->replaceAllUsesWith(CurrentMaterialization);
PN->eraseFromParent();
continue;
}
U->replaceUsesOfWith(Def, CurrentMaterialization);
}
}
}
static BasicBlock *splitBlockIfNotFirst(Instruction *I, const Twine &Name) {
auto *BB = I->getParent();
if (&BB->front() == I) {
if (BB->getSinglePredecessor()) {
BB->setName(Name);
return BB;
}
}
return BB->splitBasicBlock(I, Name);
}
static void splitAround(Instruction *I, const Twine &Name) {
splitBlockIfNotFirst(I, Name);
splitBlockIfNotFirst(I->getNextNode(), "After" + Name);
}
static bool isSuspendBlock(BasicBlock *BB) {
return isa<AnyCoroSuspendInst>(BB->front());
}
typedef SmallPtrSet<BasicBlock*, 8> VisitedBlocksSet;
static bool isSuspendReachableFrom(BasicBlock *From,
VisitedBlocksSet &VisitedOrFreeBBs) {
if (!VisitedOrFreeBBs.insert(From).second)
return false;
if (isSuspendBlock(From))
return true;
for (auto Succ : successors(From)) {
if (isSuspendReachableFrom(Succ, VisitedOrFreeBBs))
return true;
}
return false;
}
static bool isLocalAlloca(CoroAllocaAllocInst *AI) {
VisitedBlocksSet VisitedOrFreeBBs;
for (auto User : AI->users()) {
if (auto FI = dyn_cast<CoroAllocaFreeInst>(User))
VisitedOrFreeBBs.insert(FI->getParent());
}
return !isSuspendReachableFrom(AI->getParent(), VisitedOrFreeBBs);
}
static bool willLeaveFunctionImmediatelyAfter(BasicBlock *BB,
unsigned depth = 3) {
if (depth == 0) return false;
if (isSuspendBlock(BB)) return true;
for (auto Succ : successors(BB)) {
if (!willLeaveFunctionImmediatelyAfter(Succ, depth - 1))
return false;
}
return true;
}
static bool localAllocaNeedsStackSave(CoroAllocaAllocInst *AI) {
for (auto U : AI->users()) {
auto FI = dyn_cast<CoroAllocaFreeInst>(U);
if (!FI) continue;
if (!willLeaveFunctionImmediatelyAfter(FI->getParent()))
return true;
}
return false;
}
static void lowerLocalAllocas(ArrayRef<CoroAllocaAllocInst*> LocalAllocas,
SmallVectorImpl<Instruction*> &DeadInsts) {
for (auto AI : LocalAllocas) {
auto M = AI->getModule();
IRBuilder<> Builder(AI);
Value *StackSave = nullptr;
if (localAllocaNeedsStackSave(AI))
StackSave = Builder.CreateCall(
Intrinsic::getDeclaration(M, Intrinsic::stacksave));
auto Alloca = Builder.CreateAlloca(Builder.getInt8Ty(), AI->getSize());
Alloca->setAlignment(AI->getAlignment());
for (auto U : AI->users()) {
if (isa<CoroAllocaGetInst>(U)) {
U->replaceAllUsesWith(Alloca);
} else {
auto FI = cast<CoroAllocaFreeInst>(U);
if (StackSave) {
Builder.SetInsertPoint(FI);
Builder.CreateCall(
Intrinsic::getDeclaration(M, Intrinsic::stackrestore),
StackSave);
}
}
DeadInsts.push_back(cast<Instruction>(U));
}
DeadInsts.push_back(AI);
}
}
static Instruction *lowerNonLocalAlloca(CoroAllocaAllocInst *AI,
coro::Shape &Shape,
SmallVectorImpl<Instruction*> &DeadInsts) {
IRBuilder<> Builder(AI);
auto Alloc = Shape.emitAlloc(Builder, AI->getSize(), nullptr);
for (User *U : AI->users()) {
if (isa<CoroAllocaGetInst>(U)) {
U->replaceAllUsesWith(Alloc);
} else {
auto FI = cast<CoroAllocaFreeInst>(U);
Builder.SetInsertPoint(FI);
Shape.emitDealloc(Builder, Alloc, nullptr);
}
DeadInsts.push_back(cast<Instruction>(U));
}
DeadInsts.push_back(AI);
return cast<Instruction>(Alloc);
}
static Value *emitGetSwiftErrorValue(IRBuilder<> &Builder, Type *ValueTy,
coro::Shape &Shape) {
auto FnTy = FunctionType::get(ValueTy, {}, false);
auto Fn = ConstantPointerNull::get(FnTy->getPointerTo());
auto Call = Builder.CreateCall(FnTy, Fn, {});
Shape.SwiftErrorOps.push_back(Call);
return Call;
}
static Value *emitSetSwiftErrorValue(IRBuilder<> &Builder, Value *V,
coro::Shape &Shape) {
auto FnTy = FunctionType::get(V->getType()->getPointerTo(),
{V->getType()}, false);
auto Fn = ConstantPointerNull::get(FnTy->getPointerTo());
auto Call = Builder.CreateCall(FnTy, Fn, { V });
Shape.SwiftErrorOps.push_back(Call);
return Call;
}
static Value *emitSetAndGetSwiftErrorValueAround(Instruction *Call,
AllocaInst *Alloca,
coro::Shape &Shape) {
auto ValueTy = Alloca->getAllocatedType();
IRBuilder<> Builder(Call);
auto ValueBeforeCall = Builder.CreateLoad(ValueTy, Alloca);
auto Addr = emitSetSwiftErrorValue(Builder, ValueBeforeCall, Shape);
if (isa<CallInst>(Call)) {
Builder.SetInsertPoint(Call->getNextNode());
} else {
auto Invoke = cast<InvokeInst>(Call);
Builder.SetInsertPoint(Invoke->getNormalDest()->getFirstNonPHIOrDbg());
}
auto ValueAfterCall = emitGetSwiftErrorValue(Builder, ValueTy, Shape);
Builder.CreateStore(ValueAfterCall, Alloca);
return Addr;
}
static void eliminateSwiftErrorAlloca(Function &F, AllocaInst *Alloca,
coro::Shape &Shape) {
for (Use &Use : llvm::make_early_inc_range(Alloca->uses())) {
auto User = Use.getUser();
if (isa<LoadInst>(User) || isa<StoreInst>(User))
continue;
assert(isa<CallInst>(User) || isa<InvokeInst>(User));
auto Call = cast<Instruction>(User);
auto Addr = emitSetAndGetSwiftErrorValueAround(Call, Alloca, Shape);
Use.set(Addr);
}
assert(isAllocaPromotable(Alloca));
}
static void eliminateSwiftErrorArgument(Function &F, Argument &Arg,
coro::Shape &Shape,
SmallVectorImpl<AllocaInst*> &AllocasToPromote) {
IRBuilder<> Builder(F.getEntryBlock().getFirstNonPHIOrDbg());
auto ArgTy = cast<PointerType>(Arg.getType());
auto ValueTy = ArgTy->isOpaque() ? PointerType::getUnqual(F.getContext())
: ArgTy->getNonOpaquePointerElementType();
auto Alloca = Builder.CreateAlloca(ValueTy, ArgTy->getAddressSpace());
Arg.replaceAllUsesWith(Alloca);
auto InitialValue = Constant::getNullValue(ValueTy);
Builder.CreateStore(InitialValue, Alloca);
for (auto Suspend : Shape.CoroSuspends) {
(void) emitSetAndGetSwiftErrorValueAround(Suspend, Alloca, Shape);
}
for (auto End : Shape.CoroEnds) {
Builder.SetInsertPoint(End);
auto FinalValue = Builder.CreateLoad(ValueTy, Alloca);
(void) emitSetSwiftErrorValue(Builder, FinalValue, Shape);
}
AllocasToPromote.push_back(Alloca);
eliminateSwiftErrorAlloca(F, Alloca, Shape);
}
static void eliminateSwiftError(Function &F, coro::Shape &Shape) {
SmallVector<AllocaInst*, 4> AllocasToPromote;
for (auto &Arg : F.args()) {
if (!Arg.hasSwiftErrorAttr()) continue;
eliminateSwiftErrorArgument(F, Arg, Shape, AllocasToPromote);
break;
}
for (auto &Inst : F.getEntryBlock()) {
auto Alloca = dyn_cast<AllocaInst>(&Inst);
if (!Alloca || !Alloca->isSwiftError()) continue;
Alloca->setSwiftError(false);
AllocasToPromote.push_back(Alloca);
eliminateSwiftErrorAlloca(F, Alloca, Shape);
}
if (!AllocasToPromote.empty()) {
DominatorTree DT(F);
PromoteMemToReg(AllocasToPromote, DT);
}
}
static void sinkSpillUsesAfterCoroBegin(Function &F,
const FrameDataInfo &FrameData,
CoroBeginInst *CoroBegin) {
DominatorTree Dom(F);
SmallSetVector<Instruction *, 32> ToMove;
SmallVector<Instruction *, 32> Worklist;
for (auto *Def : FrameData.getAllDefs()) {
for (User *U : Def->users()) {
auto Inst = cast<Instruction>(U);
if (Inst->getParent() != CoroBegin->getParent() ||
Dom.dominates(CoroBegin, Inst))
continue;
if (ToMove.insert(Inst))
Worklist.push_back(Inst);
}
}
while (!Worklist.empty()) {
auto *Def = Worklist.pop_back_val();
for (User *U : Def->users()) {
auto Inst = cast<Instruction>(U);
if (Dom.dominates(CoroBegin, Inst))
continue;
if (ToMove.insert(Inst))
Worklist.push_back(Inst);
}
}
SmallVector<Instruction *, 64> InsertionList(ToMove.begin(), ToMove.end());
llvm::sort(InsertionList, [&Dom](Instruction *A, Instruction *B) -> bool {
return Dom.dominates(A, B);
});
Instruction *InsertPt = CoroBegin->getNextNode();
for (Instruction *Inst : InsertionList)
Inst->moveBefore(InsertPt);
}
static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape,
SuspendCrossingInfo &Checker) {
DominatorTree DT(F);
SmallPtrSet<BasicBlock *, 4> DomSet;
DomSet.insert(&F.getEntryBlock());
for (auto *CSI : Shape.CoroSuspends) {
BasicBlock *SuspendBlock = CSI->getParent();
assert(isSuspendBlock(SuspendBlock) && SuspendBlock->getSingleSuccessor() &&
"should have split coro.suspend into its own block");
DomSet.insert(SuspendBlock->getSingleSuccessor());
}
for (Instruction &I : instructions(F)) {
AllocaInst* AI = dyn_cast<AllocaInst>(&I);
if (!AI)
continue;
for (BasicBlock *DomBB : DomSet) {
bool Valid = true;
SmallVector<Instruction *, 1> Lifetimes;
auto isLifetimeStart = [](Instruction* I) {
if (auto* II = dyn_cast<IntrinsicInst>(I))
return II->getIntrinsicID() == Intrinsic::lifetime_start;
return false;
};
auto collectLifetimeStart = [&](Instruction *U, AllocaInst *AI) {
if (isLifetimeStart(U)) {
Lifetimes.push_back(U);
return true;
}
if (!U->hasOneUse() || U->stripPointerCasts() != AI)
return false;
if (isLifetimeStart(U->user_back())) {
Lifetimes.push_back(U->user_back());
return true;
}
return false;
};
for (User *U : AI->users()) {
Instruction *UI = cast<Instruction>(U);
if (!DT.dominates(DomBB, UI->getParent()) ||
Checker.isDefinitionAcrossSuspend(DomBB, UI)) {
if (collectLifetimeStart(UI, AI))
continue;
Valid = false;
break;
}
}
if (Valid && Lifetimes.size() != 0) {
auto *NewBitCast = [&](AllocaInst *AI) -> Value* {
if (isa<AllocaInst>(Lifetimes[0]->getOperand(1)))
return AI;
auto *Int8PtrTy = Type::getInt8PtrTy(F.getContext());
return CastInst::Create(Instruction::BitCast, AI, Int8PtrTy, "",
DomBB->getTerminator());
}(AI);
auto *NewLifetime = Lifetimes[0]->clone();
NewLifetime->replaceUsesOfWith(NewLifetime->getOperand(1), NewBitCast);
NewLifetime->insertBefore(DomBB->getTerminator());
for (Instruction *S : Lifetimes)
S->eraseFromParent();
break;
}
}
}
}
static void collectFrameAllocas(Function &F, coro::Shape &Shape,
const SuspendCrossingInfo &Checker,
SmallVectorImpl<AllocaInfo> &Allocas) {
for (Instruction &I : instructions(F)) {
auto *AI = dyn_cast<AllocaInst>(&I);
if (!AI)
continue;
if (AI == Shape.SwitchLowering.PromiseAlloca) {
continue;
}
DominatorTree DT(F);
bool ShouldUseLifetimeStartInfo =
(Shape.ABI != coro::ABI::Async && Shape.ABI != coro::ABI::Retcon &&
Shape.ABI != coro::ABI::RetconOnce);
AllocaUseVisitor Visitor{F.getParent()->getDataLayout(), DT,
*Shape.CoroBegin, Checker,
ShouldUseLifetimeStartInfo};
Visitor.visitPtr(*AI);
if (!Visitor.getShouldLiveOnFrame())
continue;
Allocas.emplace_back(AI, Visitor.getAliasesCopy(),
Visitor.getMayWriteBeforeCoroBegin());
}
}
void coro::salvageDebugInfo(
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
DbgVariableIntrinsic *DVI, bool OptimizeFrame) {
Function *F = DVI->getFunction();
IRBuilder<> Builder(F->getContext());
auto InsertPt = F->getEntryBlock().getFirstInsertionPt();
while (isa<IntrinsicInst>(InsertPt))
++InsertPt;
Builder.SetInsertPoint(&F->getEntryBlock(), InsertPt);
DIExpression *Expr = DVI->getExpression();
bool SkipOutermostLoad = !isa<DbgValueInst>(DVI);
Value *Storage = DVI->getVariableLocationOp(0);
Value *OriginalStorage = Storage;
while (auto *Inst = dyn_cast_or_null<Instruction>(Storage)) {
if (auto *LdInst = dyn_cast<LoadInst>(Inst)) {
Storage = LdInst->getOperand(0);
if (!SkipOutermostLoad)
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
} else if (auto *StInst = dyn_cast<StoreInst>(Inst)) {
Storage = StInst->getOperand(0);
} else {
SmallVector<uint64_t, 16> Ops;
SmallVector<Value *, 0> AdditionalValues;
Value *Op = llvm::salvageDebugInfoImpl(
*Inst, Expr ? Expr->getNumLocationOperands() : 0, Ops,
AdditionalValues);
if (!Op || !AdditionalValues.empty()) {
break;
}
Storage = Op;
Expr = DIExpression::appendOpsToArg(Expr, Ops, 0, false);
}
SkipOutermostLoad = false;
}
if (!Storage)
return;
if (!OptimizeFrame)
if (auto *Arg = dyn_cast<llvm::Argument>(Storage)) {
auto &Cached = DbgPtrAllocaCache[Storage];
if (!Cached) {
Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
Arg->getName() + ".debug");
Builder.CreateStore(Storage, Cached);
}
Storage = Cached;
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
}
DVI->replaceVariableLocationOp(OriginalStorage, Storage);
DVI->setExpression(Expr);
if (!isa<DbgValueInst>(DVI) && !isa<DbgAddrIntrinsic>(DVI)) {
if (auto *II = dyn_cast<InvokeInst>(Storage))
DVI->moveBefore(II->getNormalDest()->getFirstNonPHI());
else if (auto *CBI = dyn_cast<CallBrInst>(Storage))
DVI->moveBefore(CBI->getDefaultDest()->getFirstNonPHI());
else if (auto *InsertPt = dyn_cast<Instruction>(Storage)) {
assert(!InsertPt->isTerminator() &&
"Unimaged terminator that could return a storage.");
DVI->moveAfter(InsertPt);
} else if (isa<Argument>(Storage))
DVI->moveAfter(F->getEntryBlock().getFirstNonPHI());
}
}
void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
if (Shape.ABI != coro::ABI::Async || !Shape.CoroSuspends.empty())
eliminateSwiftError(F, Shape);
if (Shape.ABI == coro::ABI::Switch &&
Shape.SwitchLowering.PromiseAlloca) {
Shape.getSwitchCoroId()->clearPromise();
}
for (auto *CSI : Shape.CoroSuspends) {
if (auto *Save = CSI->getCoroSave())
splitAround(Save, "CoroSave");
splitAround(CSI, "CoroSuspend");
}
for (AnyCoroEndInst *CE : Shape.CoroEnds) {
splitAround(CE, "CoroEnd");
if (auto *AsyncEnd = dyn_cast<CoroAsyncEndInst>(CE)) {
auto *MustTailCallFn = AsyncEnd->getMustTailCallFunction();
if (!MustTailCallFn)
continue;
IRBuilder<> Builder(AsyncEnd);
SmallVector<Value *, 8> Args(AsyncEnd->args());
auto Arguments = ArrayRef<Value *>(Args).drop_front(3);
auto *Call = createMustTailCall(AsyncEnd->getDebugLoc(), MustTailCallFn,
Arguments, Builder);
splitAround(Call, "MustTailCall.Before.CoroEnd");
}
}
cleanupSinglePredPHIs(F);
rewritePHIs(F);
SuspendCrossingInfo Checker(F, Shape);
IRBuilder<> Builder(F.getContext());
FrameDataInfo FrameData;
SmallVector<CoroAllocaAllocInst*, 4> LocalAllocas;
SmallVector<Instruction*, 4> DeadInstructions;
{
SpillInfo Spills;
for (int Repeat = 0; Repeat < 4; ++Repeat) {
for (Instruction &I : instructions(F))
if (materializable(I)) {
for (User *U : I.users())
if (Checker.isDefinitionAcrossSuspend(I, U))
Spills[&I].push_back(cast<Instruction>(U));
}
if (Spills.empty())
break;
LLVM_DEBUG(dumpSpills("Materializations", Spills));
rewriteMaterializableInstructions(Builder, Spills);
Spills.clear();
}
}
if (Shape.ABI != coro::ABI::Async && Shape.ABI != coro::ABI::Retcon &&
Shape.ABI != coro::ABI::RetconOnce)
sinkLifetimeStartMarkers(F, Shape, Checker);
if (Shape.ABI != coro::ABI::Async || !Shape.CoroSuspends.empty())
collectFrameAllocas(F, Shape, Checker, FrameData.Allocas);
LLVM_DEBUG(dumpAllocas(FrameData.Allocas));
for (Argument &A : F.args())
for (User *U : A.users())
if (Checker.isDefinitionAcrossSuspend(A, U))
FrameData.Spills[&A].push_back(cast<Instruction>(U));
for (Instruction &I : instructions(F)) {
if (isCoroutineStructureIntrinsic(I) || &I == Shape.CoroBegin)
continue;
if (Shape.ABI == coro::ABI::Switch &&
Shape.SwitchLowering.PromiseAlloca == &I)
continue;
if (auto AI = dyn_cast<CoroAllocaAllocInst>(&I)) {
if (isLocalAlloca(AI)) {
LocalAllocas.push_back(AI);
continue;
}
auto Alloc = lowerNonLocalAlloca(AI, Shape, DeadInstructions);
for (User *U : Alloc->users()) {
if (Checker.isDefinitionAcrossSuspend(*Alloc, U))
FrameData.Spills[Alloc].push_back(cast<Instruction>(U));
}
continue;
}
if (isa<CoroAllocaGetInst>(I))
continue;
if (isa<AllocaInst>(I))
continue;
for (User *U : I.users())
if (Checker.isDefinitionAcrossSuspend(I, U)) {
if (I.getType()->isTokenTy())
report_fatal_error(
"token definition is separated from the use by a suspend point");
FrameData.Spills[&I].push_back(cast<Instruction>(U));
}
}
for (auto &Iter : FrameData.Spills) {
auto *V = Iter.first;
SmallVector<DbgValueInst *, 16> DVIs;
findDbgValues(DVIs, V);
for (DbgValueInst *DVI : DVIs)
if (Checker.isDefinitionAcrossSuspend(*V, DVI))
FrameData.Spills[V].push_back(DVI);
}
LLVM_DEBUG(dumpSpills("Spills", FrameData.Spills));
if (Shape.ABI == coro::ABI::Retcon || Shape.ABI == coro::ABI::RetconOnce ||
Shape.ABI == coro::ABI::Async)
sinkSpillUsesAfterCoroBegin(F, FrameData, Shape.CoroBegin);
Shape.FrameTy = buildFrameType(F, Shape, FrameData);
createFramePtr(Shape);
buildFrameDebugInfo(F, Shape, FrameData);
insertSpills(FrameData, Shape);
lowerLocalAllocas(LocalAllocas, DeadInstructions);
for (auto I : DeadInstructions)
I->eraseFromParent();
}