diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:11 +0000 |
commit | e3b557809604d036af6e00c60f012c2025b59a5e (patch) | |
tree | 8a11ba2269a3b669601e2fd41145b174008f4da8 /llvm/lib/Transforms/Coroutines/CoroFrame.cpp | |
parent | 08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff) |
Diffstat (limited to 'llvm/lib/Transforms/Coroutines/CoroFrame.cpp')
-rw-r--r-- | llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 273 |
1 files changed, 174 insertions, 99 deletions
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp index 51eb8ebf0369..e98c601648e0 100644 --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -37,6 +37,7 @@ #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" #include <algorithm> +#include <optional> using namespace llvm; @@ -76,11 +77,14 @@ public: // // 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' +// 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 one of the path will cross a suspend point +// 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. // namespace { struct SuspendCrossingInfo { @@ -91,6 +95,7 @@ struct SuspendCrossingInfo { BitVector Kills; bool Suspend = false; bool End = false; + bool KillLoop = false; }; SmallVector<BlockData, SmallVectorThreshold> Block; @@ -108,16 +113,31 @@ struct SuspendCrossingInfo { 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() + /// 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 { + size_t const FromIndex = Mapping.blockToIndex(From); + size_t const ToIndex = Mapping.blockToIndex(To); + bool const Result = Block[ToIndex].Kills[FromIndex]; + LLVM_DEBUG(dbgs() << From->getName() << " => " << To->getName() << " answer is " << Result << "\n"); return Result; } + /// 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 { + size_t const FromIndex = Mapping.blockToIndex(From); + size_t const ToIndex = Mapping.blockToIndex(To); + bool Result = Block[ToIndex].Kills[FromIndex] || + (From == To && Block[ToIndex].KillLoop); + LLVM_DEBUG(dbgs() << From->getName() << " => " << To->getName() + << " answer is " << Result << " (path or loop)\n"); + return Result; + } + bool isDefinitionAcrossSuspend(BasicBlock *DefBB, User *U) const { auto *I = cast<Instruction>(U); @@ -270,6 +290,7 @@ SuspendCrossingInfo::SuspendCrossingInfo(Function &F, coro::Shape &Shape) } else { // This is reached when S block it not Suspend nor coro.end and it // need to make sure that it is not in the kill set. + S.KillLoop |= S.Kills[SuccNo]; S.Kills.reset(SuccNo); } @@ -302,10 +323,10 @@ class FrameTypeBuilder; using SpillInfo = SmallMapVector<Value *, SmallVector<Instruction *, 2>, 8>; struct AllocaInfo { AllocaInst *Alloca; - DenseMap<Instruction *, llvm::Optional<APInt>> Aliases; + DenseMap<Instruction *, std::optional<APInt>> Aliases; bool MayWriteBeforeCoroBegin; AllocaInfo(AllocaInst *Alloca, - DenseMap<Instruction *, llvm::Optional<APInt>> Aliases, + DenseMap<Instruction *, std::optional<APInt>> Aliases, bool MayWriteBeforeCoroBegin) : Alloca(Alloca), Aliases(std::move(Aliases)), MayWriteBeforeCoroBegin(MayWriteBeforeCoroBegin) {} @@ -437,20 +458,20 @@ private: Align StructAlign; bool IsFinished = false; - Optional<Align> MaxFrameAlignment; + std::optional<Align> MaxFrameAlignment; SmallVector<Field, 8> Fields; DenseMap<Value*, unsigned> FieldIndexByKey; public: FrameTypeBuilder(LLVMContext &Context, const DataLayout &DL, - Optional<Align> MaxFrameAlignment) + std::optional<Align> MaxFrameAlignment) : DL(DL), Context(Context), MaxFrameAlignment(MaxFrameAlignment) {} /// Add a field to this structure for the storage of an `alloca` /// instruction. - LLVM_NODISCARD FieldIDType addFieldForAlloca(AllocaInst *AI, - bool IsHeader = false) { + [[nodiscard]] FieldIDType addFieldForAlloca(AllocaInst *AI, + bool IsHeader = false) { Type *Ty = AI->getAllocatedType(); // Make an array type if this is a static array allocation. @@ -495,9 +516,9 @@ public: coro::Shape &Shape); /// Add a field to this structure. - LLVM_NODISCARD FieldIDType addField(Type *Ty, MaybeAlign MaybeFieldAlignment, - bool IsHeader = false, - bool IsSpillOfValue = false) { + [[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"); @@ -629,8 +650,8 @@ void FrameTypeBuilder::addFieldForAllocas(const Function &F, // patterns since it just prevend putting the allocas to live in the same // slot. DenseMap<SwitchInst *, BasicBlock *> DefaultSuspendDest; - for (auto CoroSuspendInst : Shape.CoroSuspends) { - for (auto U : CoroSuspendInst->users()) { + 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(); @@ -654,10 +675,10 @@ void FrameTypeBuilder::addFieldForAllocas(const Function &F, StackLifetimeAnalyzer.getLiveRange(AI2)); }; auto GetAllocaSize = [&](const AllocaInfo &A) { - Optional<TypeSize> RetSize = A.Alloca->getAllocationSizeInBits(DL); + std::optional<TypeSize> RetSize = A.Alloca->getAllocationSize(DL); assert(RetSize && "Variable Length Arrays (VLA) are not supported.\n"); assert(!RetSize->isScalable() && "Scalable vectors are not yet supported"); - return RetSize->getFixedSize(); + return RetSize->getFixedValue(); }; // Put larger allocas in the front. So the larger allocas have higher // priority to merge, which can save more space potentially. Also each @@ -888,14 +909,15 @@ static DIType *solveDIType(DIBuilder &Builder, Type *Ty, // struct Node { // Node* ptr; // }; - RetType = Builder.createPointerType(nullptr, Layout.getTypeSizeInBits(Ty), - Layout.getABITypeAlignment(Ty), - /*DWARFAddressSpace=*/None, Name); + RetType = + Builder.createPointerType(nullptr, Layout.getTypeSizeInBits(Ty), + Layout.getABITypeAlign(Ty).value() * CHAR_BIT, + /*DWARFAddressSpace=*/std::nullopt, 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()); + Layout.getPrefTypeAlign(Ty).value() * CHAR_BIT, + llvm::DINode::FlagArtificial, nullptr, llvm::DINodeArray()); auto *StructTy = cast<StructType>(Ty); SmallVector<Metadata *, 16> Elements; @@ -1064,7 +1086,7 @@ static void buildFrameDebugInfo(Function &F, coro::Shape &Shape, Type *Ty = FrameTy->getElementType(Index); assert(Ty->isSized() && "We can't handle type which is not sized.\n"); - SizeInBits = Layout.getTypeSizeInBits(Ty).getFixedSize(); + SizeInBits = Layout.getTypeSizeInBits(Ty).getFixedValue(); AlignInBits = OffsetCache[Index].first * 8; OffsetInBits = OffsetCache[Index].second * 8; @@ -1131,13 +1153,13 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, }(); // We will use this value to cap the alignment of spilled values. - Optional<Align> MaxFrameAlignment; + std::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; + std::optional<FieldIDType> SwitchIndexFieldId; if (Shape.ABI == coro::ABI::Switch) { auto *FramePtrTy = FrameTy->getPointerTo(); @@ -1147,8 +1169,8 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, // Add header fields for the resume and destroy functions. // We can rely on these being perfectly packed. - (void)B.addField(FnPtrTy, None, /*header*/ true); - (void)B.addField(FnPtrTy, None, /*header*/ true); + (void)B.addField(FnPtrTy, std::nullopt, /*header*/ true); + (void)B.addField(FnPtrTy, std::nullopt, /*header*/ true); // PromiseAlloca field needs to be explicitly added here because it's // a header field with a fixed offset based on its alignment. Hence it @@ -1162,7 +1184,7 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, unsigned IndexBits = std::max(1U, Log2_64_Ceil(Shape.CoroSuspends.size())); Type *IndexType = Type::getIntNTy(C, IndexBits); - SwitchIndexFieldId = B.addField(IndexType, None); + SwitchIndexFieldId = B.addField(IndexType, std::nullopt); } else { assert(PromiseAlloca == nullptr && "lowering doesn't support promises"); } @@ -1178,7 +1200,7 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, // We assume that the promise alloca won't be modified before // CoroBegin and no alias will be create before CoroBegin. FrameData.Allocas.emplace_back( - PromiseAlloca, DenseMap<Instruction *, llvm::Optional<APInt>>{}, false); + PromiseAlloca, DenseMap<Instruction *, std::optional<APInt>>{}, false); // Create an entry for every spilled value. for (auto &S : FrameData.Spills) { Type *FieldType = S.first->getType(); @@ -1187,8 +1209,8 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, if (const Argument *A = dyn_cast<Argument>(S.first)) if (A->hasByValAttr()) FieldType = A->getParamByValType(); - FieldIDType Id = - B.addField(FieldType, None, false /*header*/, true /*IsSpillOfValue*/); + FieldIDType Id = B.addField(FieldType, std::nullopt, false /*header*/, + true /*IsSpillOfValue*/); FrameData.setFieldIndex(S.first, Id); } @@ -1403,7 +1425,7 @@ struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> { bool getMayWriteBeforeCoroBegin() const { return MayWriteBeforeCoroBegin; } - DenseMap<Instruction *, llvm::Optional<APInt>> getAliasesCopy() const { + DenseMap<Instruction *, std::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) @@ -1420,13 +1442,13 @@ private: // All alias to the original AllocaInst, created before CoroBegin and used // after CoroBegin. Each entry contains the instruction and the offset in the // original Alloca. They need to be recreated after CoroBegin off the frame. - DenseMap<Instruction *, llvm::Optional<APInt>> AliasOffetMap{}; + DenseMap<Instruction *, std::optional<APInt>> AliasOffetMap{}; SmallPtrSet<Instruction *, 4> Users{}; SmallPtrSet<IntrinsicInst *, 2> LifetimeStarts{}; bool MayWriteBeforeCoroBegin{false}; bool ShouldUseLifetimeStartInfo{true}; - mutable llvm::Optional<bool> ShouldLiveOnFrame{}; + mutable std::optional<bool> ShouldLiveOnFrame{}; bool computeShouldLiveOnFrame() const { // If lifetime information is available, we check it first since it's @@ -1438,6 +1460,19 @@ private: for (auto *S : LifetimeStarts) if (Checker.isDefinitionAcrossSuspend(*S, I)) return true; + // Addresses are guaranteed to be identical after every lifetime.start so + // we cannot use the local stack if the address escaped and there is a + // suspend point between lifetime markers. This should also cover the + // case of a single lifetime.start intrinsic in a loop with suspend point. + if (PI.isEscaped()) { + for (auto *A : LifetimeStarts) { + for (auto *B : LifetimeStarts) { + if (Checker.hasPathOrLoopCrossingSuspendPoint(A->getParent(), + B->getParent())) + return true; + } + } + } return false; } // FIXME: Ideally the isEscaped check should come at the beginning. @@ -1599,7 +1634,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) { // // Note: If we change the strategy dealing with alignment, we need to refine // this casting. - if (GEP->getResultElementType() != Orig->getType()) + if (GEP->getType() != Orig->getType()) return Builder.CreateBitCast(GEP, Orig->getType(), Orig->getName() + Twine(".cast")); } @@ -1777,8 +1812,15 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) { for (auto *DVI : DIs) DVI->replaceUsesOfWith(Alloca, G); - for (Instruction *I : UsersToUpdate) + for (Instruction *I : UsersToUpdate) { + // It is meaningless to remain the lifetime intrinsics refer for the + // member of coroutine frames and the meaningless lifetime intrinsics + // are possible to block further optimizations. + if (I->isLifetimeStartOrEnd()) + continue; + I->replaceUsesOfWith(Alloca, G); + } } Builder.SetInsertPoint(Shape.getInsertPtAfterFramePtr()); for (const auto &A : FrameData.Allocas) { @@ -1810,6 +1852,47 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) { AliasPtrTyped, [&](Use &U) { return DT.dominates(CB, U); }); } } + + // PromiseAlloca is not collected in FrameData.Allocas. So we don't handle + // the case that the PromiseAlloca may have writes before CoroBegin in the + // above codes. And it may be problematic in edge cases. See + // https://github.com/llvm/llvm-project/issues/57861 for an example. + if (Shape.ABI == coro::ABI::Switch && Shape.SwitchLowering.PromiseAlloca) { + AllocaInst *PA = Shape.SwitchLowering.PromiseAlloca; + // If there is memory accessing to promise alloca before CoroBegin; + bool HasAccessingPromiseBeforeCB = llvm::any_of(PA->uses(), [&](Use &U) { + auto *Inst = dyn_cast<Instruction>(U.getUser()); + if (!Inst || DT.dominates(CB, Inst)) + return false; + + if (auto *CI = dyn_cast<CallInst>(Inst)) { + // It is fine if the call wouldn't write to the Promise. + // This is possible for @llvm.coro.id intrinsics, which + // would take the promise as the second argument as a + // marker. + if (CI->onlyReadsMemory() || + CI->onlyReadsMemory(CI->getArgOperandNo(&U))) + return false; + return true; + } + + return isa<StoreInst>(Inst) || + // It may take too much time to track the uses. + // Be conservative about the case the use may escape. + isa<GetElementPtrInst>(Inst) || + // There would always be a bitcast for the promise alloca + // before we enabled Opaque pointers. And now given + // opaque pointers are enabled by default. This should be + // fine. + isa<BitCastInst>(Inst); + }); + if (HasAccessingPromiseBeforeCB) { + Builder.SetInsertPoint(Shape.getInsertPtAfterFramePtr()); + auto *G = GetFramePointer(PA); + auto *Value = Builder.CreateLoad(PA->getAllocatedType(), PA); + Builder.CreateStore(Value, G); + } + } } // Moves the values in the PHIs in SuccBB that correspong to PredBB into a new @@ -2099,7 +2182,7 @@ static bool isSuspendReachableFrom(BasicBlock *From, return true; // Recurse on the successors. - for (auto Succ : successors(From)) { + for (auto *Succ : successors(From)) { if (isSuspendReachableFrom(Succ, VisitedOrFreeBBs)) return true; } @@ -2113,7 +2196,7 @@ static bool isLocalAlloca(CoroAllocaAllocInst *AI) { // Seed the visited set with all the basic blocks containing a free // so that we won't pass them up. VisitedBlocksSet VisitedOrFreeBBs; - for (auto User : AI->users()) { + for (auto *User : AI->users()) { if (auto FI = dyn_cast<CoroAllocaFreeInst>(User)) VisitedOrFreeBBs.insert(FI->getParent()); } @@ -2133,7 +2216,7 @@ static bool willLeaveFunctionImmediatelyAfter(BasicBlock *BB, if (isSuspendBlock(BB)) return true; // Recurse into the successors. - for (auto Succ : successors(BB)) { + for (auto *Succ : successors(BB)) { if (!willLeaveFunctionImmediatelyAfter(Succ, depth - 1)) return false; } @@ -2146,7 +2229,7 @@ static bool localAllocaNeedsStackSave(CoroAllocaAllocInst *AI) { // Look for a free that isn't sufficiently obviously followed by // either a suspend or a termination, i.e. something that will leave // the coro resumption frame. - for (auto U : AI->users()) { + for (auto *U : AI->users()) { auto FI = dyn_cast<CoroAllocaFreeInst>(U); if (!FI) continue; @@ -2162,7 +2245,7 @@ static bool localAllocaNeedsStackSave(CoroAllocaAllocInst *AI) { /// instruction. static void lowerLocalAllocas(ArrayRef<CoroAllocaAllocInst*> LocalAllocas, SmallVectorImpl<Instruction*> &DeadInsts) { - for (auto AI : LocalAllocas) { + for (auto *AI : LocalAllocas) { auto M = AI->getModule(); IRBuilder<> Builder(AI); @@ -2177,7 +2260,7 @@ static void lowerLocalAllocas(ArrayRef<CoroAllocaAllocInst*> LocalAllocas, auto Alloca = Builder.CreateAlloca(Builder.getInt8Ty(), AI->getSize()); Alloca->setAlignment(AI->getAlignment()); - for (auto U : AI->users()) { + for (auto *U : AI->users()) { // Replace gets with the allocation. if (isa<CoroAllocaGetInst>(U)) { U->replaceAllUsesWith(Alloca); @@ -2340,12 +2423,12 @@ static void eliminateSwiftErrorArgument(Function &F, Argument &Arg, Builder.CreateStore(InitialValue, Alloca); // Find all the suspends in the function and save and restore around them. - for (auto Suspend : Shape.CoroSuspends) { + for (auto *Suspend : Shape.CoroSuspends) { (void) emitSetAndGetSwiftErrorValueAround(Suspend, Alloca, Shape); } // Find all the coro.ends in the function and restore the error value. - for (auto End : Shape.CoroEnds) { + for (auto *End : Shape.CoroEnds) { Builder.SetInsertPoint(End); auto FinalValue = Builder.CreateLoad(ValueTy, Alloca); (void) emitSetSwiftErrorValue(Builder, FinalValue, Shape); @@ -2523,34 +2606,32 @@ static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape, } } -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; - // The PromiseAlloca will be specially handled since it needs to be in a - // fixed position in the frame. - if (AI == Shape.SwitchLowering.PromiseAlloca) { - continue; - } - DominatorTree DT(F); - // The code that uses lifetime.start intrinsic does not work for functions - // with loops without exit. Disable it on ABIs we know to generate such - // code. - 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()); - } +static void collectFrameAlloca(AllocaInst *AI, coro::Shape &Shape, + const SuspendCrossingInfo &Checker, + SmallVectorImpl<AllocaInfo> &Allocas, + const DominatorTree &DT) { + if (Shape.CoroSuspends.empty()) + return; + + // The PromiseAlloca will be specially handled since it needs to be in a + // fixed position in the frame. + if (AI == Shape.SwitchLowering.PromiseAlloca) + return; + + // The code that uses lifetime.start intrinsic does not work for functions + // with loops without exit. Disable it on ABIs we know to generate such + // code. + bool ShouldUseLifetimeStartInfo = + (Shape.ABI != coro::ABI::Async && Shape.ABI != coro::ABI::Retcon && + Shape.ABI != coro::ABI::RetconOnce); + AllocaUseVisitor Visitor{AI->getModule()->getDataLayout(), DT, + *Shape.CoroBegin, Checker, + ShouldUseLifetimeStartInfo}; + Visitor.visitPtr(*AI); + if (!Visitor.getShouldLiveOnFrame()) + return; + Allocas.emplace_back(AI, Visitor.getAliasesCopy(), + Visitor.getMayWriteBeforeCoroBegin()); } void coro::salvageDebugInfo( @@ -2633,16 +2714,13 @@ void coro::salvageDebugInfo( // dbg.value or dbg.addr since they do not have the same function wide // guarantees that dbg.declare does. 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()); + Instruction *InsertPt = nullptr; + if (auto *I = dyn_cast<Instruction>(Storage)) + InsertPt = I->getInsertionPointAfterDef(); + else if (isa<Argument>(Storage)) + InsertPt = &*F->getEntryBlock().begin(); + if (InsertPt) + DVI->moveBefore(InsertPt); } } @@ -2687,7 +2765,7 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { } // Later code makes structural assumptions about single predecessors phis e.g - // that they are not live accross a suspend point. + // that they are not live across a suspend point. cleanupSinglePredPHIs(F); // Transforms multi-edge PHI Nodes, so that any value feeding into a PHI will @@ -2706,6 +2784,8 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { SpillInfo Spills; for (int Repeat = 0; Repeat < 4; ++Repeat) { // See if there are materializable instructions across suspend points. + // FIXME: We can use a worklist to track the possible materialize + // instructions instead of iterating the whole function again and again. for (Instruction &I : instructions(F)) if (materializable(I)) { for (User *U : I.users()) @@ -2728,28 +2808,19 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { 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)); - // Collect the spills for arguments and other not-materializable values. for (Argument &A : F.args()) for (User *U : A.users()) if (Checker.isDefinitionAcrossSuspend(A, U)) FrameData.Spills[&A].push_back(cast<Instruction>(U)); + const DominatorTree DT(F); for (Instruction &I : instructions(F)) { // Values returned from coroutine structure intrinsics should not be part // of the Coroutine Frame. if (isCoroutineStructureIntrinsic(I) || &I == Shape.CoroBegin) continue; - // The Coroutine Promise always included into coroutine frame, no need to - // check for suspend crossing. - if (Shape.ABI == coro::ABI::Switch && - Shape.SwitchLowering.PromiseAlloca == &I) - continue; - // Handle alloca.alloc specially here. if (auto AI = dyn_cast<CoroAllocaAllocInst>(&I)) { // Check whether the alloca's lifetime is bounded by suspend points. @@ -2776,8 +2847,10 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { if (isa<CoroAllocaGetInst>(I)) continue; - if (isa<AllocaInst>(I)) + if (auto *AI = dyn_cast<AllocaInst>(&I)) { + collectFrameAlloca(AI, Shape, Checker, FrameData.Allocas, DT); continue; + } for (User *U : I.users()) if (Checker.isDefinitionAcrossSuspend(I, U)) { @@ -2789,6 +2862,8 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { } } + LLVM_DEBUG(dumpAllocas(FrameData.Allocas)); + // We don't want the layout of coroutine frame to be affected // by debug information. So we only choose to salvage DbgValueInst for // whose value is already in the frame. @@ -2813,6 +2888,6 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { insertSpills(FrameData, Shape); lowerLocalAllocas(LocalAllocas, DeadInstructions); - for (auto I : DeadInstructions) + for (auto *I : DeadInstructions) I->eraseFromParent(); } |