Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ lib/CodeGen/WinEHPrepare.cpp @@ -17,6 +17,7 @@ //===----------------------------------------------------------------------===// #include "llvm/CodeGen/Passes.h" +#include "llvm/ADT/SetVector.h" #include "llvm/Analysis/CFG.h" #include "llvm/Analysis/LibCallSemantics.h" #include "llvm/CodeGen/WinEHFuncInfo.h" @@ -44,7 +45,7 @@ cl::init(false)); namespace { - + class WinEHPrepare : public FunctionPass { public: static char ID; // Pass identification, replacement for typeid. @@ -68,12 +69,26 @@ AllocaInst *insertPHILoads(PHINode *PN, Function &F); void replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot, DenseMap &Loads, Function &F); - void demoteNonlocalUses(Value *V, std::set &ColorsForBB, + void demoteNonlocalUses(Value *V, SetVector &ColorsForBB, Function &F); bool prepareExplicitEH(Function &F, SmallVectorImpl &EntryBlocks); void replaceTerminatePadWithCleanup(Function &F); void colorFunclets(Function &F, SmallVectorImpl &EntryBlocks); + void resolveFuncletAncestry(Function &F, + SmallVectorImpl &EntryBlocks); + void resolveFuncletAncestryForPath( + Function &F, SmallVectorImpl &FuncletPath, + std::map &IdentityMap); + void makeFuncletEdgeUnreachable(BasicBlock *Parent, BasicBlock *Child); + BasicBlock *cloneFuncletForParent(Function &F, BasicBlock *FuncletEntry, + BasicBlock *Parent); + void updateTerminatorsAfterFuncletClone( + Function &F, BasicBlock *OrigFunclet, BasicBlock *CloneFunclet, + BasicBlock *OrigBlock, BasicBlock *CloneBlock, BasicBlock *CloneParent, + ValueToValueMapTy &VMap, + std::map &Orig2Clone); + void demotePHIsOnFunclets(Function &F); void demoteUsesBetweenFunclets(Function &F); void demoteArgumentUses(Function &F); @@ -86,9 +101,20 @@ // All fields are reset by runOnFunction. EHPersonality Personality = EHPersonality::Unknown; - std::map> BlockColors; + std::map> BlockColors; std::map> FuncletBlocks; - std::map> FuncletChildren; + std::map> FuncletChildren; + std::map> FuncletParents; + + // This is a flag that indicates an uncommon situation where we need to + // clone funclets has been detected. + bool FuncletCloningRequired = false; + // When a funclet with multiple parents contains a catchret, the block to + // which it returns will be cloned so that there is a copy in each parent + // but one of the copies will not be properly linked to the catchret and + // in most cases will have no predecessors. This double map allows us + // to find these cloned blocks when we clone the child funclet. + std::map> EstrangedBlocks; }; } // end anonymous namespace @@ -558,9 +584,8 @@ static void colorFunclets(Function &F, SmallVectorImpl &EntryBlocks, - std::map> &BlockColors, - std::map> &FuncletBlocks, - std::map> &FuncletChildren) { + std::map> &BlockColors, + std::map> &FuncletBlocks) { SmallVector, 16> Worklist; BasicBlock *EntryBlock = &F.getEntryBlock(); @@ -577,12 +602,18 @@ // are as defined above. A post-pass fixes up the block color map to reflect // the same sense of "color" for funclet entries as for other blocks. + DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << "\nColoring funclets for " + << F.getName() << "\n"); + Worklist.push_back({EntryBlock, EntryBlock}); while (!Worklist.empty()) { BasicBlock *Visiting; BasicBlock *Color; std::tie(Visiting, Color) = Worklist.pop_back_val(); + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << "Visiting " << Visiting->getName() << ", " + << Color->getName() << "\n"); Instruction *VisitingHead = Visiting->getFirstNonPHI(); if (VisitingHead->isEHPad() && !isa(VisitingHead) && !isa(VisitingHead)) { @@ -600,8 +631,13 @@ if (auto *Exit = dyn_cast(U)) { for (BasicBlock *Succ : successors(Exit->getParent())) if (!isa(*Succ->getFirstNonPHI())) - if (BlockColors[Succ].insert(Color).second) + if (BlockColors[Succ].insert(Color)) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" + << Color->getName() << "\' to block \'" + << Succ->getName() << "\'.\n"); Worklist.push_back({Succ, Color}); + } } } // Handle CatchPad specially since its successors need different colors. @@ -609,11 +645,19 @@ // Visit the normal successor with the color of the new EH pad, and // visit the unwind successor with the color of the parent. BasicBlock *NormalSucc = CatchPad->getNormalDest(); - if (BlockColors[NormalSucc].insert(Visiting).second) { + if (BlockColors[NormalSucc].insert(Visiting)) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << Visiting->getName() + << "\' to block \'" << NormalSucc->getName() + << "\'.\n"); Worklist.push_back({NormalSucc, Visiting}); } BasicBlock *UnwindSucc = CatchPad->getUnwindDest(); - if (BlockColors[UnwindSucc].insert(Color).second) { + if (BlockColors[UnwindSucc].insert(Color)) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << Color->getName() + << "\' to block \'" << UnwindSucc->getName() + << "\'.\n"); Worklist.push_back({UnwindSucc, Color}); } continue; @@ -644,11 +688,800 @@ // safely skip this successor here. continue; } - if (BlockColors[Succ].insert(Color).second) { + if (BlockColors[Succ].insert(Color)) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << Color->getName() + << "\' to block \'" << Succ->getName() + << "\'.\n"); Worklist.push_back({Succ, Color}); } } } +} + +static BasicBlock *getEndPadForCatch(CatchPadInst *Catch) { + // The catch may have sibling catches. Follow the unwind chain until we get + // to the catchendpad. + BasicBlock *NextUnwindDest = Catch->getUnwindDest(); + auto *UnwindTerminator = NextUnwindDest->getTerminator(); + while (auto *NextCatch = dyn_cast(UnwindTerminator)) { + NextUnwindDest = NextCatch->getUnwindDest(); + UnwindTerminator = NextUnwindDest->getTerminator(); + } + // The last catch in the chain must unwind to a catchendpad. + assert(isa(UnwindTerminator)); + return NextUnwindDest; +} + +static void updateClonedEHPadUnwindToParent( + BasicBlock *UnwindDest, BasicBlock *OrigBlock, BasicBlock *CloneBlock, + std::vector &OrigParents, BasicBlock *CloneParent) { + auto updateUnwindTerminator = [](BasicBlock *BB) { + auto *Terminator = BB->getTerminator(); + if (isa(Terminator) || + isa(Terminator)) { + removeUnwindEdge(BB); + } else { + // If the block we're updating has a cleanupendpad or cleanupret + // terminator, we just want to replace that terminator with an + // unreachable instruction. + assert(isa(Terminator) || + isa(Terminator)); + // Loop over all of the successors, removing the block's entry from any + // PHI nodes. + for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE; ++SI) + (*SI)->removePredecessor(BB); + // Remove the terminator and replace it with an unreachable instruction. + BB->getTerminator()->eraseFromParent(); + new UnreachableInst(BB->getContext(), BB); + } + }; + + assert(UnwindDest->isEHPad()); + // There are many places to which this EH terminator can unwind and each has + // slightly different rules for whether or not it fits with the given + // location. + auto *EHPadInst = UnwindDest->getFirstNonPHI(); + if (isa(EHPadInst)) { + auto *CloneParentCatch = + dyn_cast(CloneParent->getFirstNonPHI()); + if (!CloneParentCatch || + getEndPadForCatch(CloneParentCatch) != UnwindDest) { + DEBUG_WITH_TYPE( + "winehprepare-coloring", + dbgs() << " removing unwind destination of clone block \'" + << CloneBlock->getName() << "\'.\n"); + updateUnwindTerminator(CloneBlock); + } + // It's possible that the catch end pad is a legal match for both the clone + // and the original, so they must be checked separately. If the original + // funclet will still have multiple parents after the current clone parent + // is removed, we'll leave its unwind terminator until later. + assert(OrigParents.size() >= 2); + if (OrigParents.size() != 2) + return; + + // If the original funclet will have a single parent after the clone parent + // is removed, check that parent's unwind destination. + assert(OrigParents.front() == CloneParent || + OrigParents.back() == CloneParent); + BasicBlock *OrigParent; + if (OrigParents.front() == CloneParent) + OrigParent = OrigParents.back(); + else + OrigParent = OrigParents.front(); + + auto *OrigParentCatch = + dyn_cast(OrigParent->getFirstNonPHI()); + if (!OrigParentCatch || getEndPadForCatch(OrigParentCatch) != UnwindDest) { + DEBUG_WITH_TYPE( + "winehprepare-coloring", + dbgs() << " removing unwind destination of original block \'" + << OrigBlock << "\'.\n"); + updateUnwindTerminator(OrigBlock); + } + } else if (auto *CleanupEnd = dyn_cast(EHPadInst)) { + // If the EH terminator unwinds to a cleanupendpad, that cleanupendpad + // must be ending a cleanuppad of either our clone parent or one + // one of the parents of the original funclet. + auto *CloneParentCP = + dyn_cast(CloneParent->getFirstNonPHI()); + auto *EndedCP = CleanupEnd->getCleanupPad(); + if (EndedCP == CloneParentCP) { + // If it is ending the cleanuppad of our cloned parent, then we + // want to remove the unwind destination of the EH terminator that + // we associated with the original funclet. + assert(isa(OrigBlock->getFirstNonPHI())); + DEBUG_WITH_TYPE( + "winehprepare-coloring", + dbgs() << " removing unwind destination of original block \'" + << OrigBlock->getName() << "\'.\n"); + updateUnwindTerminator(OrigBlock); + } else { + // If it isn't ending the cleanuppad of our clone parent, then we + // want to remove the unwind destination of the EH terminator that + // associated with our cloned funclet. + assert(isa(CloneBlock->getFirstNonPHI())); + DEBUG_WITH_TYPE( + "winehprepare-coloring", + dbgs() << " removing unwind destination of clone block \'" + << CloneBlock->getName() << "\'.\n"); + updateUnwindTerminator(CloneBlock); + } + } else { + // If the EH terminator unwinds to a catchpad, cleanuppad or + // terminatepad that EH pad must be a sibling of the funclet we're + // cloning. We'll clone it later and update one of the catchendpad + // instrunctions that unwinds to it at that time. + assert(isa(EHPadInst) || isa(EHPadInst) || + isa(EHPadInst)); + } +} + +// If the terminator is a catchpad, we must also clone the catchendpad to which +// it unwinds and add this to the clone parent's block list. The catchendpad +// unwinds to either its caller, a sibling EH pad, a cleanup end pad in its +// parent funclet or a catch end pad in its grandparent funclet (which must be +// coupled with the parent funclet). If it has no unwind destination +// (i.e. unwind to caller), there is nothing to be done. If the unwind +// destination is a sibling EH pad, we will update the terminators later (in +// resolveFuncletAncestryForPath). If it unwinds to a cleanup end pad or a +// catch end pad and this end pad corresponds to the clone parent, we will +// remove the unwind destination in the original catchendpad. If it unwinds to +// a cleanup end pad or a catch end pad that does not correspond to the clone +// parent, we will remove the unwind destination in the cloned catchendpad. +static void updateCatchTerminators( + Function &F, CatchPadInst *OrigCatch, CatchPadInst *CloneCatch, + std::vector &OrigParents, BasicBlock *CloneParent, + ValueToValueMapTy &VMap, + std::map> &BlockColors, + std::map> &FuncletBlocks) { + // If we're cloning a catch pad that unwinds to a catchendpad, we also + // need to clone the catchendpad. The coloring algorithm associates + // the catchendpad block with the funclet's parent, so we have some work + // to do here to figure out whether the original belongs to the clone + // parent or one of the original funclets other parents (it might have + // more than one at this point). In either case, we might also need to + // remove the unwind edge if the catchendpad doesn't unwind to a block + // in the right grandparent funclet. + Instruction *I = CloneCatch->getUnwindDest()->getFirstNonPHI(); + if (auto *CEP = dyn_cast(I)) { + assert(BlockColors[CEP->getParent()].size() == 1); + BasicBlock *CEPFunclet = *(BlockColors[CEP->getParent()].begin()); + BasicBlock *CEPCloneParent = nullptr; + CatchPadInst *PredCatch = nullptr; + if (CEPFunclet == CloneParent) { + // The catchendpad is in the clone parent, so we need to clone it + // and associate the clone with the original funclet's parent. If + // the original funclet had multiple parents, we'll add it to the + // first parent that isn't the clone parent. The logic in + // updateClonedEHPadUnwindToParent() will only remove the unwind edge + // if there is only one parent other than the clone parent, so we don't + // need to verify the ancestry. The catchendpad will eventually be + // cloned into the correct parent and all invalid unwind edges will be + // removed. + for (auto *Parent : OrigParents) { + if (Parent != CloneParent) { + CEPCloneParent = Parent; + break; + } + } + PredCatch = OrigCatch; + } else { + CEPCloneParent = CloneParent; + PredCatch = CloneCatch; + } + assert(CEPCloneParent && PredCatch); + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Cloning catchendpad \'" + << CEP->getParent()->getName() << "\' for funclet \'" + << CEPCloneParent->getName() << "\'.\n"); + BasicBlock *ClonedCEP = CloneBasicBlock( + CEP->getParent(), VMap, Twine(".from.", CEPCloneParent->getName())); + // Insert the clone immediately after the original to ensure determinism + // and to keep the same relative ordering of any funclet's blocks. + ClonedCEP->insertInto(&F, CEP->getParent()->getNextNode()); + PredCatch->setUnwindDest(ClonedCEP); + FuncletBlocks[CEPCloneParent].insert(ClonedCEP); + BlockColors[ClonedCEP].insert(CEPCloneParent); + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigning color \'" + << CEPCloneParent->getName() << "\' to block \'" + << ClonedCEP->getName() << "\'.\n"); + auto *ClonedCEPInst = cast(ClonedCEP->getTerminator()); + if (auto *Dest = ClonedCEPInst->getUnwindDest()) + updateClonedEHPadUnwindToParent(Dest, OrigCatch->getUnwindDest(), + CloneCatch->getUnwindDest(), OrigParents, + CloneParent); + } +} + +// While we are cloning a funclet because it has multiple parents, we will call +// this routine to update the terminators for the original and cloned copies +// of each basic block. All blocks in the funclet have been clone by this time. +// OrigBlock and CloneBlock will be identical except for their block label. +// +// If the terminator is a catchpad, we must also clone the catchendpad to which +// it unwinds and in most cases update either the original catchendpad or the +// clone. See the updateCatchTerminators() helper routine for details. +// +// If the terminator is a catchret its successor is a block in its parent +// funclet. If the instruction returns to a block in the parent for which the +// cloned funclet was created, the terminator in the original block must be +// replaced by an unreachable instruction. Otherwise the terminator in the +// clone block must be replaced by an unreachable instruction. +// +// If the terminator is a cleanupret or cleanupendpad it either unwinds to +// caller or unwinds to a sibling EH pad, a cleanup end pad in its parent +// funclet or a catch end pad in its grandparent funclet (which must be +// coupled with the parent funclet). If it unwinds to caller there is +// nothing to be done. If the unwind destination is a sibling EH pad, we will +// update the terminators later (in resolveFuncletAncestryForPath). If it +// unwinds to a cleanup end pad or a catch end pad and this end pad corresponds +// to the clone parent, we will replace the terminator in the original block +// with an unreachable instruction. If it unwinds to a cleanup end pad or a +// catch end pad that does not correspond to the clone parent, we will replace +// the terminator in the clone block with an unreachable instruction. +// +// If the terminator is an invoke instruction, we will handle it after all +// siblings of the current funclet have been cloned. +void WinEHPrepare::updateTerminatorsAfterFuncletClone( + Function &F, BasicBlock *OrigFunclet, BasicBlock *CloneFunclet, + BasicBlock *OrigBlock, BasicBlock *CloneBlock, BasicBlock *CloneParent, + ValueToValueMapTy &VMap, std::map &Orig2Clone) { + // If the cloned block doesn't have an exceptional terminator, there is + // nothing to be done here. + TerminatorInst *CloneTerminator = CloneBlock->getTerminator(); + if (!CloneTerminator->isExceptional()) + return; + + if (auto *CloneCatch = dyn_cast(CloneTerminator)) { + // A cloned catch pad has a lot of wrinkles, so we'll call a helper function + // to update this case. + auto *OrigCatch = cast(OrigBlock->getTerminator()); + updateCatchTerminators(F, OrigCatch, CloneCatch, + FuncletParents[OrigFunclet], CloneParent, VMap, + BlockColors, FuncletBlocks); + } else if (auto *CRI = dyn_cast(CloneTerminator)) { + if (FuncletBlocks[CloneParent].count(CRI->getSuccessor())) { + BasicBlock *OrigParent; + // The original funclet may have more than two parents, but that's OK. + // We just need to remap the original catchret to any of the parents. + // All of the parents should have an entry in the EstrangedBlocks map + // if any of them do. + if (FuncletParents[OrigFunclet].front() == CloneParent) + OrigParent = FuncletParents[OrigFunclet].back(); + else + OrigParent = FuncletParents[OrigFunclet].front(); + for (succ_iterator SI = succ_begin(OrigBlock), SE = succ_end(OrigBlock); + SI != SE; ++SI) + (*SI)->removePredecessor(OrigBlock); + BasicBlock *LostBlock = EstrangedBlocks[OrigParent][CRI->getSuccessor()]; + auto *OrigCatchRet = cast(OrigBlock->getTerminator()); + if (LostBlock) { + OrigCatchRet->setSuccessor(LostBlock); + } else { + OrigCatchRet->eraseFromParent(); + new UnreachableInst(OrigBlock->getContext(), OrigBlock); + } + } else { + for (succ_iterator SI = succ_begin(CloneBlock), SE = succ_end(CloneBlock); + SI != SE; ++SI) + (*SI)->removePredecessor(CloneBlock); + BasicBlock *LostBlock = EstrangedBlocks[CloneParent][CRI->getSuccessor()]; + if (LostBlock) { + CRI->setSuccessor(LostBlock); + } else { + CRI->eraseFromParent(); + new UnreachableInst(CloneBlock->getContext(), CloneBlock); + } + } + } else if (isa(CloneTerminator) || + isa(CloneTerminator)) { + BasicBlock *UnwindDest = nullptr; + + // A cleanup pad can unwind through either a cleanupret or a cleanupendpad + // but both are handled the same way. + if (auto *CRI = dyn_cast(CloneTerminator)) + UnwindDest = CRI->getUnwindDest(); + else if (auto *CEI = dyn_cast(CloneTerminator)) + UnwindDest = CEI->getUnwindDest(); + + // If the instruction has no local unwind destination, there is nothing + // to be done. + if (!UnwindDest) + return; + + // The unwind destination may be a sibling EH pad, a catchendpad in + // a grandparent funclet (ending a catchpad in the parent) or a cleanup + // cleanupendpad in the parent. Call a helper routine to diagnose this + // and remove either the clone or original terminator as needed. + updateClonedEHPadUnwindToParent(UnwindDest, OrigBlock, CloneBlock, + FuncletParents[OrigFunclet], CloneParent); + } +} + +// Clones all blocks used by the specified funclet to avoid the funclet having +// multiple parent funclets. All terminators in the parent that unwind to the +// original funclet are remapped to unwind to the clone. Any terminator in the +// original funclet which returned to this parent is converted to an unreachable +// instruction. Likewise, any terminator in the cloned funclet which returns to +// a parent funclet other than the specified parent is converted to an +// unreachable instruction. +BasicBlock *WinEHPrepare::cloneFuncletForParent(Function &F, + BasicBlock *FuncletEntry, + BasicBlock *Parent) { + std::set &BlocksInFunclet = FuncletBlocks[FuncletEntry]; + + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << "Cloning funclet \'" << FuncletEntry->getName() + << "\' for parent \'" << Parent->getName() << "\'.\n"); + + std::map Orig2Clone; + ValueToValueMapTy VMap; + for (BasicBlock *BB : BlocksInFunclet) { + // Create a new basic block and copy instructions into it. + BasicBlock *CBB = + CloneBasicBlock(BB, VMap, Twine(".from.", Parent->getName())); + + // Insert the clone immediately after the original to ensure determinism + // and to keep the same relative ordering of any funclet's blocks. + CBB->insertInto(&F, BB->getNextNode()); + + // Add basic block mapping. + VMap[BB] = CBB; + + // Record delta operations that we need to perform to our color mappings. + Orig2Clone[BB] = CBB; + } // end for (BasicBlock *BB : BlocksInFunclet) + + BasicBlock *ClonedFunclet = Orig2Clone[FuncletEntry]; + assert(ClonedFunclet); + + // Set the coloring for the blocks we just cloned. + std::set &ClonedBlocks = FuncletBlocks[ClonedFunclet]; + for (auto &BBMapping : Orig2Clone) { + BasicBlock *NewBlock = BBMapping.second; + ClonedBlocks.insert(NewBlock); + BlockColors[NewBlock].insert(ClonedFunclet); + + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigning color \'" << ClonedFunclet->getName() + << "\' to block \'" << NewBlock->getName() + << "\'.\n"); + + // Use the VMap to remap the instructions in this cloned block. + for (Instruction &I : *NewBlock) + RemapInstruction(&I, VMap, RF_IgnoreMissingEntries); + } + + // All the cloned blocks have to be colored in the loop above before we can + // update the terminators because doing so can require checking the color of + // other blocks in the cloned funclet. + for (auto &BBMapping : Orig2Clone) { + BasicBlock *OldBlock = BBMapping.first; + BasicBlock *NewBlock = BBMapping.second; + + // Update the terminator, if necessary, in both the original block and the + // cloned so that the original funclet never returns to a block in the + // clone parent and the clone funclet never returns to a block in any other + // of the original funclet's parents. + updateTerminatorsAfterFuncletClone(F, FuncletEntry, ClonedFunclet, OldBlock, + NewBlock, Parent, VMap, Orig2Clone); + + // Check to see if the cloned block successor has PHI nodes. If so, we need + // to add entries to the PHI nodes for the cloned block now. + for (BasicBlock *SuccBB : successors(NewBlock)) { + for (Instruction &SuccI : *SuccBB) { + auto *SuccPN = dyn_cast(&SuccI); + if (!SuccPN) + break; + + // Ok, we have a PHI node. Figure out what the incoming value was for + // the OldBlock. + int OldBlockIdx = SuccPN->getBasicBlockIndex(OldBlock); + if (OldBlockIdx == -1) + break; + Value *IV = SuccPN->getIncomingValue(OldBlockIdx); + + // Remap the value if necessary. + if (auto *Inst = dyn_cast(IV)) { + ValueToValueMapTy::iterator I = VMap.find(Inst); + if (I != VMap.end()) + IV = I->second; + } + + SuccPN->addIncoming(IV, NewBlock); + } + } + } + + // Erase the clone's parent from the original funclet's parent list. + std::vector &Parents = FuncletParents[FuncletEntry]; + Parents.erase(std::remove(Parents.begin(), Parents.end(), Parent), + Parents.end()); + + // Store the cloned funclet's parent. + assert(std::find(FuncletParents[ClonedFunclet].begin(), + FuncletParents[ClonedFunclet].end(), + Parent) == std::end(FuncletParents[ClonedFunclet])); + FuncletParents[ClonedFunclet].push_back(Parent); + + // Copy any children of the original funclet to the clone. We'll either + // clone them too or make that path unreachable when we take the next step + // in resolveFuncletAncestryForPath(). + for (auto *Child : FuncletChildren[FuncletEntry]) { + assert(std::find(FuncletChildren[ClonedFunclet].begin(), + FuncletChildren[ClonedFunclet].end(), + Child) == std::end(FuncletChildren[ClonedFunclet])); + FuncletChildren[ClonedFunclet].push_back(Child); + assert(std::find(FuncletParents[Child].begin(), FuncletParents[Child].end(), + ClonedFunclet) == std::end(FuncletParents[Child])); + FuncletParents[Child].push_back(ClonedFunclet); + } + + // Find any blocks that unwound to the original funclet entry from the + // clone parent block and remap them to the clone. + for (auto *U : FuncletEntry->users()) { + auto *UT = dyn_cast(U); + if (!UT) + continue; + BasicBlock *UBB = UT->getParent(); + assert(BlockColors[UBB].size() == 1); + BasicBlock *UFunclet = *(BlockColors[UBB].begin()); + // Funclets shouldn't be able to loop back on themselves. + assert(UFunclet != FuncletEntry); + // If this instruction unwinds to the original funclet from the clone + // parent, remap the terminator so that it unwinds to the clone instead. + // We will perform a similar transformation for siblings after all + // the siblings have been cloned. + if (UFunclet == Parent) { + // We're about to break the path from this block to the uncloned funclet + // entry, so remove it as a predeccessor to clean up the PHIs. + FuncletEntry->removePredecessor(UBB); + TerminatorInst *Terminator = UBB->getTerminator(); + RemapInstruction(Terminator, VMap, RF_IgnoreMissingEntries); + } + } + + // This asserts a condition that is relied upon inside the loop below, + // namely that no predecessors of the original funclet entry block + // are also predecessors of the cloned funclet entry block. + assert(std::all_of(pred_begin(FuncletEntry), pred_end(FuncletEntry), + [&ClonedFunclet](BasicBlock *Pred) { + return std::find(pred_begin(ClonedFunclet), + pred_end(ClonedFunclet), + Pred) == pred_end(ClonedFunclet); + })); + + // Remove any invalid PHI node entries in the cloned funclet.cl + std::vector PHIsToErase; + for (Instruction &I : *ClonedFunclet) { + auto *PN = dyn_cast(&I); + if (!PN) + break; + + // Predecessors of the original funclet do not reach the cloned funclet, + // but the cloning process assumes they will. Remove them now. + for (auto *Pred : predecessors(FuncletEntry)) + PN->removeIncomingValue(Pred, false); + } + for (auto *PN : PHIsToErase) + PN->eraseFromParent(); + + // Replace the original funclet in the parent's children vector with the + // cloned funclet. + for (auto &It : FuncletChildren[Parent]) { + if (It == FuncletEntry) { + It = ClonedFunclet; + break; + } + } + + return ClonedFunclet; +} + +// Removes the unwind edge for any exceptional terminators within the specified +// parent funclet that previously unwound to the specified child funclet. +void WinEHPrepare::makeFuncletEdgeUnreachable(BasicBlock *Parent, + BasicBlock *Child) { + for (BasicBlock *BB : FuncletBlocks[Parent]) { + TerminatorInst *Terminator = BB->getTerminator(); + if (!Terminator->isExceptional()) + continue; + + // Look for terninators that unwind to the child funclet. + BasicBlock *UnwindDest = nullptr; + if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + else if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + else if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + // cleanupendpad, catchret and cleanupret don't represent a parent-to-child + // funclet transition, so we don't need to consider them here. + + // If the child funclet is the unwind destination, replace the terminator + // with an unreachable instruction. + if (UnwindDest == Child) + removeUnwindEdge(BB); + } + // The specified parent is no longer a parent of the specified child. + std::vector &Children = FuncletChildren[Parent]; + Children.erase(std::remove(Children.begin(), Children.end(), Child), + Children.end()); +} + +// This routine is called after funclets with multiple parents are cloned for +// a specific parent. Here we look for children of the specified funclet that +// unwind to other children of that funclet and update the unwind destinations +// to ensure that each sibling is connected to the correct clone of the sibling +// to which it unwinds. +// +// If the terminator is an invoke instruction, it unwinds either to a child +// EH pad, a cleanup end pad in the current funclet, or a catch end pad in a +// parent funclet (which ends either the current catch pad or a sibling +// catch pad). If it unwinds to a child EH pad, the child will have multiple +// parents after this funclet is cloned and this case will be handled later in +// the resolveFuncletAncestryForPath processing. If it unwinds to a +// cleanup end pad in the current funclet, the instruction remapping during +// the cloning process should have already mapped the unwind destination to +// the cloned copy of the cleanup end pad. If it unwinds to a catch end pad +// there are two possibilities: either the catch end pad is the unwind +// destination for the catch pad we are currently cloning or it is the unwind +// destination for a sibling catch pad. If it it the unwind destination of the +// catch pad we are cloning, we need to update the cloned invoke instruction +// to unwind to the cloned catch end pad. Otherwise, we will handle this +// later (in resolveFuncletAncestryForPath). +static void updateSiblingToSiblingUnwind( + BasicBlock *CurFunclet, + std::map> &BlockColors, + std::map> &FuncletBlocks, + std::map> &FuncletParents, + std::map> &FuncletChildren, + std::map &Funclet2Orig) { + // Remap any bad sibling-to-sibling transitions for funclets that + // we just cloned. + for (BasicBlock *ChildFunclet : FuncletChildren[CurFunclet]) { + for (auto *BB : FuncletBlocks[ChildFunclet]) { + TerminatorInst *Terminator = BB->getTerminator(); + if (!Terminator->isExceptional()) + continue; + + // See if this terminator has an unwind destination. + // Note that catchendpads are handled when the associated catchpad + // is cloned. They don't fit the pattern we're looking for here. + BasicBlock *UnwindDest = nullptr; + if (auto *I = dyn_cast(Terminator)) { + UnwindDest = I->getUnwindDest(); + // The catchendpad is not a sibling destination. This case should + // have been handled in cloneFuncletForParent(). + if (isa(Terminator)) { + assert(BlockColors[UnwindDest].size() == 1 && + "Cloned catchpad unwinds to an pad with multiple parents."); + assert(FuncletParents[UnwindDest].front() == CurFunclet && + "Cloned catchpad unwinds to the wrong parent."); + continue; + } + } else { + if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + else if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + + // If the cleanup unwinds to caller, there is nothing to be done. + if (!UnwindDest) + continue; + } + + // If the destination is not a cleanup pad, catch pad or terminate pad + // we don't need to handle it here. + Instruction *EHPad = UnwindDest->getFirstNonPHI(); + if (!isa(EHPad) && !isa(EHPad) && + !isa(EHPad)) + continue; + + // If it is one of these, then it is either a sibling of the current + // child funclet or a clone of one of those siblings. + // If it is a sibling, no action is needed. + if (FuncletParents[UnwindDest].size() == 1 && + FuncletParents[UnwindDest].front() == CurFunclet) + continue; + + // If the unwind destination is a clone of a sibling, we need to figure + // out which sibling it is a clone of and use that sibling as the + // unwind destination. + BasicBlock *DestOrig = Funclet2Orig[UnwindDest]; + BasicBlock *TargetSibling = nullptr; + for (auto &Mapping : Funclet2Orig) { + if (Mapping.second != DestOrig) + continue; + BasicBlock *MappedFunclet = Mapping.first; + if (FuncletParents[MappedFunclet].size() == 1 && + FuncletParents[MappedFunclet].front() == CurFunclet) { + TargetSibling = MappedFunclet; + } + } + // If we didn't find the sibling we were looking for then the + // unwind destination is not a clone of one of child's siblings. + // That's unexpected. + assert(TargetSibling && "Funclet unwinds to unexpected destination."); + + // Update the terminator instruction to unwind to the correct sibling. + if (auto *I = dyn_cast(Terminator)) + I->setUnwindDest(TargetSibling); + else if (auto *I = dyn_cast(Terminator)) + I->setUnwindDest(TargetSibling); + else if (auto *I = dyn_cast(Terminator)) + I->setUnwindDest(TargetSibling); + } + } + + // Invoke remapping can't be done correctly until after all of their + // other sibling-to-sibling unwinds have been remapped. + for (BasicBlock *ChildFunclet : FuncletChildren[CurFunclet]) { + bool NeedOrigInvokeRemapping = false; + for (auto *BB : FuncletBlocks[ChildFunclet]) { + TerminatorInst *Terminator = BB->getTerminator(); + auto *II = dyn_cast(Terminator); + if (!II) + continue; + + BasicBlock *UnwindDest = II->getUnwindDest(); + assert(UnwindDest && "Invoke unwinds to a null destination."); + assert(UnwindDest->isEHPad() && "Invoke does not unwind to an EH pad."); + auto *EHPadInst = UnwindDest->getFirstNonPHI(); + if (isa(EHPadInst)) { + // An invoke that unwinds to a cleanup end pad must be in a cleanup pad. + assert(isa(ChildFunclet->getFirstNonPHI()) && + "Unwinding to cleanup end pad from a non cleanup pad funclet."); + // The funclet cloning should have remapped the destination to the cloned + // cleanup end pad. + assert(FuncletBlocks[ChildFunclet].count(UnwindDest) && + "Unwind destination for invoke was not updated during cloning."); + } else if (isa(EHPadInst)) { + // If the invoke unwind destination is the unwind destination for + // the current child catch pad funclet, there is nothing to be done. + BasicBlock *OrigFunclet = Funclet2Orig[ChildFunclet]; + auto *CurCatch = cast(ChildFunclet->getFirstNonPHI()); + auto *OrigCatch = cast(OrigFunclet->getFirstNonPHI()); + if (OrigCatch->getUnwindDest() == UnwindDest) { + // If the invoke unwinds to a catch end pad that is the unwind + // destination for the original catch pad, the cloned invoke should + // unwind to the cloned catch end pad. + II->setUnwindDest(CurCatch->getUnwindDest()); + } else if (CurCatch->getUnwindDest() == UnwindDest) { + // If the invoke unwinds to a catch end pad that is the unwind + // destination for the clone catch pad, the original invoke should + // unwind to the unwind destination of the original catch pad. + // This happens when the catch end pad is matched to the clone + // parent when the catchpad instruction is cloned and the original + // invoke instruction unwinds to the original catch end pad (which + // is now the unwind destination of the cloned catch pad). + NeedOrigInvokeRemapping = true; + } else { + // Otherwise, the invoke unwinds to a catch end pad that is the unwind + // destination another catch pad in the unwind chain from either the + // current catch pad or one of its clones. If it is already the + // catch end pad at the end unwind chain from the current catch pad, + // we'll need to check the invoke instructions in the original funclet + // later. Otherwise, we need to remap this invoke now. + assert((getEndPadForCatch(OrigCatch) == UnwindDest || + getEndPadForCatch(CurCatch) == UnwindDest) && + "Invoke within catch pad unwinds to an invalid catch end pad."); + BasicBlock *CurCatchEnd = getEndPadForCatch(CurCatch); + if (CurCatchEnd == UnwindDest) + NeedOrigInvokeRemapping = true; + else + II->setUnwindDest(CurCatchEnd); + } + } + } + if (NeedOrigInvokeRemapping) { + // To properly remap invoke instructions that unwind to catch end pads + // that are not the unwind destination of the catch pad funclet in which + // the invoke appears, we must also look at the uncloned invoke in the + // original funclet. If we saw an invoke that was already properly + // unwinding to a sibling's catch end pad, we need to check the invokes + // in the original funclet. + BasicBlock *OrigFunclet = Funclet2Orig[ChildFunclet]; + for (auto *BB : FuncletBlocks[OrigFunclet]) { + auto *II = dyn_cast(BB->getTerminator()); + if (!II) + continue; + + BasicBlock *UnwindDest = II->getUnwindDest(); + assert(UnwindDest && "Invoke unwinds to a null destination."); + assert(UnwindDest->isEHPad() && "Invoke does not unwind to an EH pad."); + auto *CEP = dyn_cast(UnwindDest->getFirstNonPHI()); + if (!CEP) + continue; + + // If the invoke unwind destination is the unwind destination for + // the original catch pad funclet, there is nothing to be done. + auto *OrigCatch = cast(OrigFunclet->getFirstNonPHI()); + + // If the invoke unwinds to a catch end pad that is neither the unwind + // destination of OrigCatch or the destination another catch pad in the + // unwind chain from current catch pad, we need to remap the invoke. + BasicBlock *OrigCatchEnd = getEndPadForCatch(OrigCatch); + if (OrigCatchEnd != UnwindDest) + II->setUnwindDest(OrigCatchEnd); + } + } + } +} + +void WinEHPrepare::resolveFuncletAncestry( + Function &F, SmallVectorImpl &EntryBlocks) { + // Most of the time this will be unnecessary. If the conditions arise that + // require this work, this flag will be set. + if (!FuncletCloningRequired) + return; + + // Funclet2Orig is used to map any cloned funclets back to the original + // funclet from which they were cloned. The map is seeded with the + // original funclets mapping to themselves. + std::map Funclet2Orig; + for (auto *Funclet : EntryBlocks) + Funclet2Orig[Funclet] = Funclet; + + // Start with the entry funclet and walk the funclet parent-child tree. + SmallVector FuncletPath; + FuncletPath.push_back(&(F.getEntryBlock())); + resolveFuncletAncestryForPath(F, FuncletPath, Funclet2Orig); +} + +// Walks the funclet control flow, cloning any funclets that have more than one +// parent funclet and breaking any cyclic unwind chains so that the path becomes +// unreachable at the point where a funclet would have unwound to a funclet that +// was already in the chain. +void WinEHPrepare::resolveFuncletAncestryForPath( + Function &F, SmallVectorImpl &FuncletPath, + std::map &Funclet2Orig) { + bool ClonedAnyChildren = false; + BasicBlock *CurFunclet = FuncletPath.back(); + // Copy the children vector because we might changing it. + std::vector Children(FuncletChildren[CurFunclet]); + for (BasicBlock *ChildFunclet : Children) { + // Don't allow the funclet chain to unwind back on itself. + // If this funclet is already in the current funclet chain, make the + // path to it through the current funclet unreachable. + bool IsCyclic = false; + BasicBlock *ChildIdentity = Funclet2Orig[ChildFunclet]; + for (BasicBlock *Ancestor : FuncletPath) { + BasicBlock *AncestorIdentity = Funclet2Orig[Ancestor]; + if (AncestorIdentity == ChildIdentity) { + IsCyclic = true; + break; + } + } + // If the unwind chain wraps back on itself, break the chain. + if (IsCyclic) { + makeFuncletEdgeUnreachable(CurFunclet, ChildFunclet); + continue; + } + // If this child funclet has other parents, clone the entire funclet. + if (FuncletParents[ChildFunclet].size() > 1) { + ChildFunclet = cloneFuncletForParent(F, ChildFunclet, CurFunclet); + Funclet2Orig[ChildFunclet] = ChildIdentity; + ClonedAnyChildren = true; + } + FuncletPath.push_back(ChildFunclet); + resolveFuncletAncestryForPath(F, FuncletPath, Funclet2Orig); + FuncletPath.pop_back(); + } + // If we didn't clone any children, we can return now. + if (!ClonedAnyChildren) + return; + + updateSiblingToSiblingUnwind(CurFunclet, BlockColors, FuncletBlocks, + FuncletParents, FuncletChildren, Funclet2Orig); +} + +void WinEHPrepare::colorFunclets(Function &F, + SmallVectorImpl &EntryBlocks) { + ::colorFunclets(F, EntryBlocks, BlockColors, FuncletBlocks); // The processing above actually accumulated the parent set for this // funclet into the color set for its entry; use the parent set to @@ -656,19 +1489,28 @@ // the funclet itself (no instruction can target a funclet entry except on // that transitions to the child funclet). for (BasicBlock *FuncletEntry : EntryBlocks) { - std::set &ColorMapItem = BlockColors[FuncletEntry]; - for (BasicBlock *Parent : ColorMapItem) - FuncletChildren[Parent].insert(FuncletEntry); + SetVector &ColorMapItem = BlockColors[FuncletEntry]; + // It will be rare for funclets to have multiple parents, but if any + // do we need to clone the funclet later to address that. Here we + // set a flag indicating that this case has arisen so that we don't + // have to do a lot of checking later to handle the more common case. + if (ColorMapItem.size() > 1) + FuncletCloningRequired = true; + for (BasicBlock *Parent : ColorMapItem) { + assert(std::find(FuncletChildren[Parent].begin(), + FuncletChildren[Parent].end(), + FuncletEntry) == std::end(FuncletChildren[Parent])); + FuncletChildren[Parent].push_back(FuncletEntry); + assert(std::find(FuncletParents[FuncletEntry].begin(), + FuncletParents[FuncletEntry].end(), + Parent) == std::end(FuncletParents[FuncletEntry])); + FuncletParents[FuncletEntry].push_back(Parent); + } ColorMapItem.clear(); ColorMapItem.insert(FuncletEntry); } } -void WinEHPrepare::colorFunclets(Function &F, - SmallVectorImpl &EntryBlocks) { - ::colorFunclets(F, EntryBlocks, BlockColors, FuncletBlocks, FuncletChildren); -} - void llvm::calculateCatchReturnSuccessorColors(const Function *Fn, WinEHFuncInfo &FuncInfo) { SmallVector EntryBlocks; @@ -676,12 +1518,20 @@ // findFuncletEntryPoints. findFuncletEntryPoints(const_cast(*Fn), EntryBlocks); - std::map> BlockColors; + std::map> BlockColors; std::map> FuncletBlocks; - std::map> FuncletChildren; // Figure out which basic blocks belong to which funclets. colorFunclets(const_cast(*Fn), EntryBlocks, BlockColors, - FuncletBlocks, FuncletChildren); + FuncletBlocks); + + // The static colorFunclets routine assigns multiple colors to funclet entries + // because that information is needed to calculate funclets' parent-child + // relationship, but we don't need those relationship here and ultimately the + // entry blocks should have the color of the funclet they begin. + for (BasicBlock *FuncletEntry : EntryBlocks) { + BlockColors[FuncletEntry].clear(); + BlockColors[FuncletEntry].insert(FuncletEntry); + } // We need to find the catchret successors. To do this, we must first find // all the catchpad funclets. @@ -700,7 +1550,7 @@ if (!CatchReturn) continue; BasicBlock *CatchRetSuccessor = CatchReturn->getSuccessor(); - std::set &SuccessorColors = BlockColors[CatchRetSuccessor]; + SetVector &SuccessorColors = BlockColors[CatchRetSuccessor]; assert(SuccessorColors.size() == 1 && "Expected BB to be monochrome!"); BasicBlock *Color = *SuccessorColors.begin(); // Record the catchret successor's funclet membership. @@ -742,7 +1592,7 @@ // Turn all inter-funclet uses of a Value into loads and stores. for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) { BasicBlock *BB = &*FI++; - std::set &ColorsForBB = BlockColors[BB]; + SetVector &ColorsForBB = BlockColors[BB]; for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) { Instruction *I = &*BI++; // Funclets are permitted to use static allocas. @@ -757,7 +1607,7 @@ void WinEHPrepare::demoteArgumentUses(Function &F) { // Also demote function parameters used in funclets. - std::set &ColorsForEntry = BlockColors[&F.getEntryBlock()]; + SetVector &ColorsForEntry = BlockColors[&F.getEntryBlock()]; for (Argument &Arg : F.args()) demoteNonlocalUses(&Arg, ColorsForEntry, F); } @@ -772,13 +1622,71 @@ std::map Orig2Clone; ValueToValueMapTy VMap; - for (BasicBlock *BB : BlocksInFunclet) { - std::set &ColorsForBB = BlockColors[BB]; + for (auto BlockIt = BlocksInFunclet.begin(), + BlockEnd = BlocksInFunclet.end(); + BlockIt != BlockEnd;) { + // Increment the iterator inside the loop because we might be removing + // blocks from the set. + BasicBlock *BB = *BlockIt++; + SetVector &ColorsForBB = BlockColors[BB]; // We don't need to do anything if the block is monochromatic. size_t NumColorsForBB = ColorsForBB.size(); if (NumColorsForBB == 1) continue; + // If this block is a catchendpad, it shouldn't be cloned. + // We will only see a catchendpad with multiple colors in the case where + // some funclet has multiple parents. In that case, the color will be + // resolved during the resolveFuncletAncestry processing. + // For now, find the catchpad that unwinds to this block and assign + // that catchpad's first parent to be the color for this block. + if (isa(BB->getFirstNonPHI())) { + assert( + FuncletCloningRequired && + "Found multi-colored catchendpad with no multi-parent funclets."); + BasicBlock *CatchParent = nullptr; + // There can only be one catchpad predecessor for a catchendpad. + for (BasicBlock *PredBB : predecessors(BB)) { + if (isa(PredBB->getTerminator())) { + CatchParent = PredBB; + break; + } + } + // There must be one catchpad predecessor for a catchendpad. + assert(CatchParent && "No catchpad found for catchendpad."); + + // If the catchpad has multiple parents, we'll clone the catchendpad + // when we clone the catchpad funclet and insert it into the correct + // funclet. For now, we just select the first parent of the catchpad + // and give the catchendpad that color. + BasicBlock *CorrectColor = FuncletParents[CatchParent].front(); + assert(FuncletBlocks[CorrectColor].count(BB)); + assert(BlockColors[BB].count(CorrectColor)); + + // Remove this block from the FuncletBlocks set of any funclet that + // isn't the funclet whose color we just selected. + for (auto It = BlockColors[BB].begin(), End = BlockColors[BB].end(); + It != End; ) { + // The iterator must be incremented here because we are removing + // elements from the set we're walking. + auto Temp = It++; + BasicBlock *ContainingFunclet = *Temp; + if (ContainingFunclet != CorrectColor) { + FuncletBlocks[ContainingFunclet].erase(BB); + BlockColors[BB].remove(ContainingFunclet); + } + } + + // This should leave just one color for BB. + assert(BlockColors[BB].size() == 1); + continue; + } + + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Cloning block \'" << BB->getName() + << "\' for funclet \'" << FuncletPadBB->getName() + << "\'.\n"); + // Create a new basic block and copy instructions into it! BasicBlock *CBB = CloneBasicBlock(BB, VMap, Twine(".for.", FuncletPadBB->getName())); @@ -806,8 +1714,52 @@ BlocksInFunclet.insert(NewBlock); BlockColors[NewBlock].insert(FuncletPadBB); + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << FuncletPadBB->getName() + << "\' to block \'" << NewBlock->getName() + << "\'.\n"); + BlocksInFunclet.erase(OldBlock); - BlockColors[OldBlock].erase(FuncletPadBB); + BlockColors[OldBlock].remove(FuncletPadBB); + + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Removed color \'" << FuncletPadBB->getName() + << "\' from block \'" << OldBlock->getName() + << "\'.\n"); + + // If we are cloning a funclet that might share a child funclet with + // another funclet, look to see if the cloned block is reached from a + // catchret instruction. If so, save this association so we can retrieve + // the possibly orphaned clone when we clone the child funclet. + if (FuncletCloningRequired) { + for (auto *Pred : predecessors(OldBlock)) { + auto *Terminator = Pred->getTerminator(); + if (!isa(Terminator)) + continue; + // If this block is reached from a catchret instruction in a funclet + // that has multiple parents, it will have a color for each of those + // parents. We just removed the color of one of the parents, but + // the cloned block will be unreachable until we clone the child + // funclet that contains the catchret instruction. In that case we + // need to create a mapping that will let us find the cloned block + // later and associate it with the cloned child funclet. + bool BlockWillBeEstranged = false; + for (auto *Color : BlockColors[Pred]) { + if (FuncletParents[Color].size() > 1) { + BlockWillBeEstranged = true; + break; // Breaks out of the color loop + } + } + if (BlockWillBeEstranged) { + EstrangedBlocks[FuncletPadBB][OldBlock] = NewBlock; + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Saved mapping of estranged block \'" + << NewBlock->getName() << "\' for \'" + << FuncletPadBB->getName() << "\'.\n"); + break; // Breaks out of the predecessor loop + } + } + } } // Loop over all of the instructions in this funclet, fixing up operand @@ -865,7 +1817,7 @@ for (Use &U : OldI->uses()) { Instruction *UserI = cast(U.getUser()); BasicBlock *UserBB = UserI->getParent(); - std::set &ColorsForUserBB = BlockColors[UserBB]; + SetVector &ColorsForUserBB = BlockColors[UserBB]; assert(!ColorsForUserBB.empty()); if (ColorsForUserBB.size() > 1 || *ColorsForUserBB.begin() != FuncletPadBB) @@ -994,6 +1946,8 @@ cloneCommonBlocks(F, EntryBlocks); + resolveFuncletAncestry(F, EntryBlocks); + if (!DisableCleanups) { removeImplausibleTerminators(F); @@ -1005,6 +1959,9 @@ BlockColors.clear(); FuncletBlocks.clear(); FuncletChildren.clear(); + FuncletParents.clear(); + EstrangedBlocks.clear(); + FuncletCloningRequired = false; return true; } @@ -1099,10 +2056,24 @@ new StoreInst(PredVal, SpillSlot, PredBlock->getTerminator()); } +// The SetVector == operator uses the std::vector == operator, so it doesn't +// actually tell us whether or not the two sets contain the same colors. This +// function does that. +// FIXME: Would it be better to add a isSetEquivalent() method to SetVector? +static bool isBlockColorSetEquivalent(SetVector &SetA, + SetVector &SetB) { + if (SetA.size() != SetB.size()) + return false; + for (auto *Color : SetA) + if (!SetB.count(Color)) + return false; + return true; +} + // TODO: Share loads for same-funclet uses (requires dominators if funclets // aren't properly nested). void WinEHPrepare::demoteNonlocalUses(Value *V, - std::set &ColorsForBB, + SetVector &ColorsForBB, Function &F) { // Tokens can only be used non-locally due to control flow involving // unreachable edges. Don't try to demote the token usage, we'll simply @@ -1120,8 +2091,8 @@ // Is the Use inside a block which is colored the same as the Def? // If so, we don't need to escape the Def because we will clone // ourselves our own private copy. - std::set &ColorsForUsingBB = BlockColors[UsingBB]; - if (ColorsForUsingBB == ColorsForBB) + SetVector &ColorsForUsingBB = BlockColors[UsingBB]; + if (isBlockColorSetEquivalent(ColorsForUsingBB, ColorsForBB)) continue; replaceUseWithLoad(V, U, SpillSlot, Loads, F); @@ -1145,7 +2116,7 @@ BasicBlock *NewBlock = SplitCriticalEdge(II, SuccNum); assert(NewBlock && "Unable to split critical edge."); // Update the color mapping for the newly split edge. - std::set &ColorsForUsingBB = BlockColors[II->getParent()]; + SetVector &ColorsForUsingBB = BlockColors[II->getParent()]; BlockColors[NewBlock] = ColorsForUsingBB; for (BasicBlock *FuncletPad : ColorsForUsingBB) FuncletBlocks[FuncletPad].insert(NewBlock); @@ -1212,7 +2183,7 @@ Goto->setSuccessor(0, PHIBlock); CatchRet->setSuccessor(NewBlock); // Update the color mapping for the newly split edge. - std::set &ColorsForPHIBlock = BlockColors[PHIBlock]; + SetVector &ColorsForPHIBlock = BlockColors[PHIBlock]; BlockColors[NewBlock] = ColorsForPHIBlock; for (BasicBlock *FuncletPad : ColorsForPHIBlock) FuncletBlocks[FuncletPad].insert(NewBlock); Index: test/CodeGen/WinEH/wineh-cloning.ll =================================================================== --- test/CodeGen/WinEH/wineh-cloning.ll +++ test/CodeGen/WinEH/wineh-cloning.ll @@ -280,7 +280,30 @@ ; the dynamic path enters %left, then enters %inner, ; then calls @h, and that the call to @h doesn't return. ; CHECK-LABEL: define void @test6( -; TODO: CHECKs +; CHECK: left: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: %x = call i32 @g() +; CHECK: store i32 %x, i32* %x.wineh.spillslot +; CHECK: to label %shared.cont unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: shared.cont: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_R:\%.+]] = cleanuppad [] +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: cleanupret [[I_R]] unwind label %right.end +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_L:\%.+]] = cleanuppad [] +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable define void @test7() personality i32 (...)* @__CxxFrameHandler3 { @@ -312,7 +335,32 @@ ; with the join at the entry itself instead of following a ; non-pad join. ; CHECK-LABEL: define void @test7( -; TODO: CHECKs +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %right +; CHECK: left: +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: to label %unreachable unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_R:\%.+]] = cleanuppad [] +; CHECK: [[X_R:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X_R]]) +; CHECK: cleanupret [[I_R]] unwind label %right.end +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_L:\%.+]] = cleanuppad [] +; CHECK: [[X_L:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X_L]]) +; CHECK: unreachable +; CHECK: unreachable: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable define void @test8() personality i32 (...)* @__CxxFrameHandler3 { @@ -350,7 +398,40 @@ ; %inner is a two-parent child which itself has a child; need ; to make two copies of both the %inner and %inner.child. ; CHECK-LABEL: define void @test8( -; TODO: CHECKs +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %right +; CHECK: left: +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_RIGHT]]: +; CHECK: to label %[[UNREACHABLE_INNER_RIGHT:.+]] unwind label %[[INNER_CHILD_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: to label %[[UNREACHABLE_INNER_LEFT:.+]] unwind label %[[INNER_CHILD_LEFT:.+]] +; CHECK: [[INNER_CHILD_RIGHT]]: +; CHECK: [[TMP:\%.+]] = cleanuppad [] +; CHECK: [[X:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X]]) +; CHECK: unreachable +; CHECK: [[INNER_CHILD_LEFT]]: +; CHECK: [[TMP:\%.+]] = cleanuppad [] +; CHECK: [[X:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X]]) +; CHECK: unreachable +; CHECK: [[UNREACHABLE_INNER_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_INNER_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable define void @test9() personality i32 (...)* @__CxxFrameHandler3 { @@ -383,7 +464,33 @@ ; of which was which along the way; generating each possibility lets ; whichever case was correct execute correctly. ; CHECK-LABEL: define void @test9( -; TODO: CHECKs +; CHECK: entry: +; CHECK: to label %invoke.cont unwind label %[[LEFT:.+]] +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %[[RIGHT:.+]] +; CHECK: [[LEFT_FROM_RIGHT:.+]]: +; CHECK: call void @h(i32 1) +; CHECK: call void @f() +; CHECK: unreachable +; CHECK: [[LEFT]]: +; CHECK: call void @h(i32 1) +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[RIGHT_FROM_LEFT:.+]] +; CHECK: [[RIGHT]]: +; CHECK: call void @h(i32 2) +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[LEFT_FROM_RIGHT]] +; CHECK: [[RIGHT_FROM_LEFT]]: +; CHECK: call void @h(i32 2) +; CHECK: call void @f() +; CHECK: unreachable +; CHECK: [[UNREACHABLE_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable + define void @test10() personality i32 (...)* @__CxxFrameHandler3 { entry: Index: test/CodeGen/WinEH/wineh-demotion.ll =================================================================== --- test/CodeGen/WinEH/wineh-demotion.ll +++ test/CodeGen/WinEH/wineh-demotion.ll @@ -86,18 +86,18 @@ ; CHECK: store i32 %z ; CHECK-NEXT: invoke void @f invoke void @f() - to label %catchret.inner unwind label %merge.outer + to label %catchret.inner unwind label %catchend.inner catchret.inner: catchret %cpinner to label %exit catchend.inner: + ; CHECK-NOT: = phi + %y = phi i32 [ %x, %merge.inner ], [ %z, %catch.inner ] catchendpad unwind label %merge.outer merge.outer: ; CHECK: merge.outer: - ; CHECK-NOT: = phi ; CHECK: [[CatchPad:%[^ ]+]] = catchpad [] - %y = phi i32 [ %x, %catchend.inner ], [ %z, %catch.inner ] %cpouter = catchpad [] to label %catch.outer unwind label %catchend.outer catchend.outer: Index: test/CodeGen/WinEH/wineh-multi-parent-cloning.ll =================================================================== --- test/CodeGen/WinEH/wineh-multi-parent-cloning.ll +++ test/CodeGen/WinEH/wineh-multi-parent-cloning.ll @@ -0,0 +1,1557 @@ +; RUN: opt -mtriple=x86_x64-pc-windows-msvc -S -winehprepare < %s | FileCheck %s + +declare i32 @__CxxFrameHandler3(...) + +declare void @f() +declare i32 @g() +declare void @h(i32) +declare i1 @b() + +define void @test1() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %exit unwind label %right +left: + cleanuppad [] + br label %shared +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + %i = cleanuppad [] + call void @h(i32 %x) + cleanupret %i unwind label %right.end +exit: + ret void +} +; %inner is a cleanup which appears both as a child of +; %left and as a child of %right. Since statically we +; need each funclet to have a single parent, we need to +; clone the entire %inner funclet so we can have one +; copy under each parent. The cleanupret in %inner +; unwinds to the catchendpad for %right, so the copy +; of %inner under %right should include it; the copy +; of %inner under %left should instead have an +; `unreachable` inserted there, but the copy under +; %left still needs to be created because it's possible +; the dynamic path enters %left, then enters %inner, +; then calls @h, and that the call to @h doesn't return. +; CHECK-LABEL: define void @test1( +; CHECK: left: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: %x = call i32 @g() +; CHECK: store i32 %x, i32* %x.wineh.spillslot +; CHECK: to label %shared.cont unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: shared.cont: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_R:\%.+]] = cleanuppad [] +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: cleanupret [[I_R]] unwind label %right.end +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_L:\%.+]] = cleanuppad [] +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable + + +define void @test2() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %exit unwind label %right +left: + cleanuppad [] + br label %shared +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 %x) + unreachable +inner.end: + catchendpad unwind label %right.end +exit: + ret void +} +; In this case left and right are both parents of inner. This differs from +; @test1 in that inner is a catchpad rather than a cleanuppad, which makes +; inner.end a block that gets cloned so that left and right each contain a +; copy (catchendpad blocks are considered to be part of the parent funclet +; of the associated catchpad). The catchendpad in %inner.end unwinds to +; %right.end (which belongs to the entry funclet). +; CHECK-LABEL: define void @test2( +; CHECK: left: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]] +; CHECK: right.catch: +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind label %[[RIGHT_END]] + +define void @test3() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + %l = cleanuppad [] + br label %shared +left.end: + cleanupendpad %l unwind label %right +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 %x) + unreachable +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; In this case, %left and %right are siblings with %entry as the parent of both, +; while %left and %right are both parents of %inner. The catchendpad in +; %inner.end unwinds to %left.end. When %inner is cloned a copy of %inner.end +; will be made for both %left and %right, but because %left.end is a cleanup pad +; and %right is a catch pad the unwind edge from the copy of %inner.end for +; %right must be removed. +; CHECK-LABEL: define void @test3( +; CHECK: left: +; CHECK: %l = cleanuppad [] +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: [[LEFT_END:left.end.*]]: +; CHECK: cleanupendpad %l unwind label %right +; CHECK: right: +; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]] +; CHECK: right.catch: +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller + + +define void @test4() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + catchpad [] + to label %left.catch unwind label %left.end +left.catch: + br label %shared +left.end: + catchendpad unwind label %right +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 %x) + unreachable +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; This is a variation of @test3 in which both %left and %right are catch pads. +; In this case, %left and %right are siblings with %entry as the parent of both, +; while %left and %right are both parents of %inner. The catchendpad in +; %inner.end unwinds to %left.end. When %inner is cloned a copy of %inner.end +; will be made for both %left and %right, but because the catchpad in %right +; does not unwind to %left.end the unwind edge from the copy of %inner.end for +; %right must be removed. +; CHECK-LABEL: define void @test4( +; CHECK: left: +; CHECK: catchpad [] +; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]] +; CHECK: left.catch: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: [[LEFT_END]]: +; CHECK: catchendpad unwind label %right +; CHECK: right: +; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]] +; CHECK: right.catch: +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] + + +define void @test5() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + catchpad [] + to label %left.catch unwind label %left.end +left.catch: + br label %shared +left.end: + catchendpad unwind label %right +right: + %r = cleanuppad [] + br label %shared +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 %x) + unreachable +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; Like @test3, %left and %right are siblings with %entry as the parent of both, +; while %left and %right are both parents of %inner. This case makes %left a +; catch and %right a cleanup so that %inner unwinds to %left.end, which is a +; block in %entry. The %inner funclet is cloned for %left and %right, but the +; copy of %inner.end for %right must have its unwind edge removed because the +; catchendpad at %left.end is not compatible with %right. +; CHECK-LABEL: define void @test5( +; CHECK: left: +; CHECK: catchpad [] +; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]] +; CHECK: left.catch: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: [[LEFT_END]]: +; CHECK: catchendpad unwind label %right +; CHECK: right: +; CHECK: %r = cleanuppad [] +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] + +define void @test6() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + catchpad [] + to label %left.catch unwind label %left.end +left.catch: + br label %shared +left.end: + catchendpad unwind label %middle +middle: + %m = catchpad [] + to label %middle.catch unwind label %middle.end +middle.catch: + catchret %m to label %exit +middle.end: + catchendpad unwind label %right +right: + %r = cleanuppad [] + br label %shared +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 %x) + unreachable +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; This is like @test5 but it inserts another sibling between %left and %right. +; In this case %left, %middle and %right are all siblings, while %left and +; %right are both parents of %inner. This checks the proper handling of the +; catchendpad in %inner.end (which will be cloned so that %left and %right both +; have copies) unwinding to a catchendpad that unwinds to a sibling. +; CHECK-LABEL: define void @test6( +; CHECK: left: +; CHECK: catchpad [] +; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]] +; CHECK: left.catch: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: [[LEFT_END]]: +; CHECK: catchendpad unwind label %middle +; CHECK: middle: +; CHECK: catchpad [] +; CHECK: to label %middle.catch unwind label %middle.end +; CHECK: middle.catch: +; CHECK: catchret %m to label %exit +; CHECK: middle.end: +; CHECK: catchendpad unwind label %right +; CHECK: right: +; CHECK: %r = cleanuppad [] +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] + + +define void @test7() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + catchpad [] + to label %left.catch unwind label %left.end +left.catch: + br label %shared +left.end: + catchendpad unwind label %right +right: + %r = cleanuppad [] + br label %shared +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 %x) + unreachable +inner.end: + catchendpad unwind label %inner.sibling +inner.sibling: + %is = cleanuppad [] + call void @h(i32 0) + cleanupret %is unwind label %left.end +exit: + ret void +} +; This is like @test5 but instead of unwinding to %left.end, the catchendpad +; in %inner.end unwinds to a sibling cleanup pad. Both %inner (along with its +; associated blocks) and %inner.sibling must be cloned for %left and %right. +; The clones of %inner will be identical, but the copy of %inner.sibling for +; %right must end with an unreachable instruction, because it cannot unwind to +; %left.end. +; CHECK-LABEL: define void @test7( +; CHECK: left: +; CHECK: catchpad [] +; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]] +; CHECK: left.catch: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: [[LEFT_END]]: +; CHECK: catchendpad unwind label %[[RIGHT:.+]] +; CHECK: [[RIGHT]]: +; CHECK: [[R:\%.+]] = cleanuppad [] +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind label %[[INNER_SIBLING_RIGHT:.+]] +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[INNER_SIBLING_LEFT:.+]] +; CHECK: [[INNER_SIBLING_RIGHT]] +; CHECK: [[IS_R:\%.+]] = cleanuppad [] +; CHECK: call void @h(i32 0) +; CHECK: unreachable +; CHECK: [[INNER_SIBLING_LEFT]] +; CHECK: [[IS_L:\%.+]] = cleanuppad [] +; CHECK: call void @h(i32 0) +; CHECK: cleanupret [[IS_L]] unwind label %[[LEFT_END]] + + +define void @test8() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %unreachable unwind label %right +left: + cleanuppad [] + invoke void @f() to label %unreachable unwind label %inner +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + invoke void @f() to label %unreachable unwind label %inner +right.end: + catchendpad unwind to caller +inner: + %i = cleanuppad [] + %x = call i32 @g() + call void @h(i32 %x) + cleanupret %i unwind label %right.end +unreachable: + unreachable +} +; Another case of a two-parent child (like @test1), this time +; with the join at the entry itself instead of following a +; non-pad join. +; CHECK-LABEL: define void @test8( +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %right +; CHECK: left: +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: to label %unreachable unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_R:\%.+]] = cleanuppad [] +; CHECK: [[X_R:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X_R]]) +; CHECK: cleanupret [[I_R]] unwind label %right.end +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_L:\%.+]] = cleanuppad [] +; CHECK: [[X_L:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X_L]]) +; CHECK: unreachable +; CHECK: unreachable: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable + + +define void @test9() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %unreachable unwind label %right +left: + cleanuppad [] + br label %shared +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + invoke void @f() + to label %unreachable unwind label %inner +inner: + cleanuppad [] + invoke void @f() + to label %unreachable unwind label %inner.child +inner.child: + cleanuppad [] + %x = call i32 @g() + call void @h(i32 %x) + unreachable +unreachable: + unreachable +} +; %inner is a two-parent child which itself has a child; need +; to make two copies of both the %inner and %inner.child. +; CHECK-LABEL: define void @test9( +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %right +; CHECK: left: +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_RIGHT]]: +; CHECK: to label %[[UNREACHABLE_INNER_RIGHT:.+]] unwind label %[[INNER_CHILD_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: to label %[[UNREACHABLE_INNER_LEFT:.+]] unwind label %[[INNER_CHILD_LEFT:.+]] +; CHECK: [[INNER_CHILD_RIGHT]]: +; CHECK: [[TMP:\%.+]] = cleanuppad [] +; CHECK: [[X:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X]]) +; CHECK: unreachable +; CHECK: [[INNER_CHILD_LEFT]]: +; CHECK: [[TMP:\%.+]] = cleanuppad [] +; CHECK: [[X:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X]]) +; CHECK: unreachable +; CHECK: [[UNREACHABLE_INNER_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_INNER_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable + + +define void @test10() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %unreachable unwind label %right +left: + cleanuppad [] + call void @h(i32 1) + invoke void @f() + to label %unreachable unwind label %right +right: + cleanuppad [] + call void @h(i32 2) + invoke void @f() + to label %unreachable unwind label %left +unreachable: + unreachable +} +; This is an irreducible loop with two funclets that enter each other; +; need to make two copies of each funclet (one a child of root, the +; other a child of the opposite funclet), but also make sure not to +; clone self-descendants (if we tried to do that we'd need to make an +; infinite number of them). Presumably if optimizations ever generated +; such a thing it would mean that one of the two cleanups was originally +; the parent of the other, but that we'd somehow lost track in the CFG +; of which was which along the way; generating each possibility lets +; whichever case was correct execute correctly. +; CHECK-LABEL: define void @test10( +; CHECK: entry: +; CHECK: to label %invoke.cont unwind label %[[LEFT:.+]] +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %[[RIGHT:.+]] +; CHECK: [[LEFT_FROM_RIGHT:.+]]: +; CHECK: call void @h(i32 1) +; CHECK: call void @f() +; CHECK: unreachable +; CHECK: [[LEFT]]: +; CHECK: call void @h(i32 1) +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[RIGHT_FROM_LEFT:.+]] +; CHECK: [[RIGHT]]: +; CHECK: call void @h(i32 2) +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[LEFT_FROM_RIGHT]] +; CHECK: [[RIGHT_FROM_LEFT]]: +; CHECK: call void @h(i32 2) +; CHECK: call void @f() +; CHECK: unreachable +; CHECK: [[UNREACHABLE_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable + + +define void @test11() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + catchpad [] + to label %left.catch unwind label %left.sibling +left.catch: + br label %shared +left.sibling: + %ls = catchpad [] + to label %left.sibling.catch unwind label %left.end +left.sibling.catch: + catchret %ls to label %exit +left.end: + catchendpad unwind label %right +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 %x) + unreachable +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; This is a variation of @test4 in which the shared child funclet unwinds to a +; catchend pad that is the unwind destination of %left.sibling rather than %left +; but is still a valid destination for %inner as reach from %left. +; When %inner is cloned a copy of %inner.end will be made for both %left and +; %right, but because the catchpad in %right does not unwind to %left.end the +; unwind edge from the copy of %inner.end for %right must be removed. +; CHECK-LABEL: define void @test11( +; CHECK: left: +; CHECK: catchpad [] +; CHECK: to label %left.catch unwind label %left.sibling +; CHECK: left.catch: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: left.sibling: +; CHECK: catchpad [] +; CHECK: to label %left.sibling.catch unwind label %[[LEFT_END:.+]] +; CHECK: [[LEFT_END]]: +; CHECK: catchendpad unwind label %right +; CHECK: right: +; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]] +; CHECK: right.catch: +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] + + +define void @test12() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + catchpad [] + to label %left.catch unwind label %right +left.catch: + br label %shared +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + %x = call i32 @g() + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 %x) + unreachable +inner.end: + catchendpad unwind label %right.end +exit: + ret void +} +; In this case %left and %right are both parents of %inner, so %inner must be +; cloned but the catchendpad unwind target in %inner.end is valid for both +; parents, so the unwind edge should not be removed in either case. +; CHECK-LABEL: define void @test12( +; CHECK: left: +; CHECK: catchpad [] +; CHECK: to label %left.catch unwind label %right +; CHECK: left.catch: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]] +; CHECK: right.catch: +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind label %[[RIGHT_END]] +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[RIGHT_END]] + +define void @test13() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %exit unwind label %right +left: + %l = catchpad [] + to label %left.cont unwind label %left.end +left.cont: + invoke void @f() + to label %left.ret unwind label %inner +left.ret: + catchret %l to label %invoke.cont +left.end: + catchendpad unwind to caller +right: + %r = catchpad [] + to label %right.catch unwind label %right.end +right.catch: + invoke void @f() + to label %right.ret unwind label %inner +right.ret: + catchret %r to label %exit +right.end: + catchendpad unwind to caller +shared: + call void @h(i32 0) + unreachable +inner: + %i = catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 1) + catchret %i to label %shared +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; This case tests the scenario where a funclet with multiple parents uses a +; catchret to return to a block that may exist in either parent funclets. +; Both %left and %right are parents of %inner. During common block cloning +; a clone of %shared will be made so that both %left and %right have a copy, +; but the copy of %shared for one of the parent funclets will be unreachable +; until the %inner funclet is cloned. When the %inner.catch block is cloned +; during the %inner funclet cloning, the catchret instruction should be updated +; so that the catchret in the copy %inner.catch for %left returns to the copy of +; %shared in %left and the catchret in the copy of %inner.catch for %right +; returns to the copy of %shared for %right. +; CHECK-LABEL: define void @test13( +; CHECK: left: +; CHECK: %l = catchpad [] +; CHECK: to label %left.cont unwind label %left.end +; CHECK: left.cont: +; CHECK: invoke void @f() +; CHECK: to label %left.ret unwind label %[[INNER_LEFT:.+]] +; CHECK: left.ret: +; CHECK: catchret %l to label %invoke.cont +; CHECK: left.end: +; CHECK: catchendpad unwind to caller +; CHECK: right: +; CHECK: %r = catchpad [] +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: invoke void @f() +; CHECK: to label %right.ret unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.ret: +; CHECK: catchret %r to label %exit +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_RIGHT:.+]]: +; CHECK: call void @h(i32 0) +; CHECK: unreachable +; CHECK: [[SHARED_LEFT:.+]]: +; CHECK: call void @h(i32 0) +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: %[[I_RIGHT:.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: %[[I_LEFT:.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: call void @h(i32 1) +; CHECK: catchret %[[I_RIGHT]] to label %[[SHARED_RIGHT]] +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: call void @h(i32 1) +; CHECK: catchret %[[I_LEFT]] to label %[[SHARED_LEFT]] +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller + + +define void @test14() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + %l = catchpad [] + to label %shared unwind label %left.end +left.cont: + invoke void @f() + to label %left.ret unwind label %right +left.ret: + catchret %l to label %exit +left.end: + catchendpad unwind to caller +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind label %left.end +shared: + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + %i = catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 0) + catchret %i to label %left.cont +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; This case tests another scenario where a funclet with multiple parents uses a +; catchret to return to a block in one of the parent funclets. Here %right and +; %left are both parents of %inner and %left is a parent of %right. The +; catchret in %inner.catch will cause %left.cont and %left.ret to be cloned for +; both %left and %right, but the catchret in %left.ret is invalid for %right +; but the catchret instruction in the copy of %left.ret for %right will be +; removed as an implausible terminator. +; CHECK-LABEL: define void @test14( +; CHECK: left: +; CHECK: %l = catchpad [] +; CHECK: to label %[[SHARED_LEFT:.+]] unwind label %[[LEFT_END:.+]] +; CHECK: [[LEFT_CONT:left.cont.*]]: +; CHECK: invoke void @f() +; CHECK: to label %[[LEFT_RET:.+]] unwind label %[[RIGHT:.+]] +; CHECK: [[LEFT_RET]]: +; CHECK: catchret %l to label %exit +; CHECK: [[LEFT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]] +; CHECK: [[RIGHT_CATCH]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] +; CHECK: [[SHARED_LEFT]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_LEFT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_RIGHT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: call void @h(i32 0) +; CHECK: catchret [[I_LEFT]] to label %[[LEFT_CONT]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: call void @h(i32 0) +; CHECK: unreachable +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller + +define void @test15() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + %l = catchpad [] + to label %left.catch unwind label %left.end +left.catch: + invoke void @f() + to label %shared unwind label %right +left.ret: + catchret %l to label %exit +left.end: + catchendpad unwind to caller +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind label %left.end +shared: + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + %i = catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 0) + catchret %i to label %left.ret +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; This case is a variation of test14 but instead of returning to an invoke the +; catchret in %inner.catch returns to a catchret instruction. +; CHECK-LABEL: define void @test15( +; CHECK: left: +; CHECK: %l = catchpad [] +; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]] +; CHECK: left.catch: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_LEFT:.+]] unwind label %[[RIGHT:.+]] +; CHECK: [[LEFT_RET_RIGHT:.+]]: +; CHECK: unreachable +; CHECK: [[LEFT_RET_LEFT:.+]]: +; CHECK: catchret %l to label %exit +; CHECK: [[LEFT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]] +; CHECK: [[RIGHT_CATCH]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] +; CHECK: [[SHARED_LEFT]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_LEFT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_RIGHT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: call void @h(i32 0) +; CHECK: catchret [[I_LEFT]] to label %[[LEFT_RET_LEFT]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: call void @h(i32 0) +; CHECK: catchret [[I_RIGHT]] to label %[[LEFT_RET_RIGHT]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] + + +define void @test16() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %exit unwind label %left +left: + %l = cleanuppad [] + br label %shared +left.cont: + cleanupret %l unwind label %right +left.end: + cleanupendpad %l unwind label %right +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + invoke void @f() + to label %shared.cont unwind label %inner +shared.cont: + unreachable +inner: + %i = catchpad [] + to label %inner.catch unwind label %inner.end +inner.catch: + call void @h(i32 0) + catchret %i to label %left.cont +inner.end: + catchendpad unwind label %left.end +exit: + ret void +} +; This case is another variation of test14 but here the catchret in %inner.catch +; returns to a cleanupret instruction. +; CHECK-LABEL: define void @test16( +; CHECK: left: +; CHECK: %l = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: [[LEFT_CONT_RIGHT:.+]]: +; CHECK: unreachable +; CHECK: [[LEFT_CONT_LEFT:.+]]: +; CHECK: cleanupret %l unwind label %[[RIGHT:.+]] +; CHECK: [[LEFT_END_LEFT:.+]]: +; CHECK: cleanupendpad %l unwind label %[[RIGHT]] +; CHECK: [[RIGHT]]: +; CHECK: catchpad [] +; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]] +; CHECK: [[RIGHT_CATCH]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_RIGHT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_LEFT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: call void @h(i32 0) +; CHECK: catchret [[I_RIGHT]] to label %[[LEFT_CONT_RIGHT]] +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: call void @h(i32 0) +; CHECK: catchret [[I_LEFT]] to label %[[LEFT_CONT_LEFT]] +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END_LEFT]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller + + +define void @test17() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %exit unwind label %right +left: + %l = cleanuppad [] + br label %shared +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + invoke void @f() + to label %unreachable unwind label %inner +unreachable: + unreachable +inner: + %i = catchpad [] + to label %inner.catch unwind label %inner.sibling +inner.catch: + call void @h(i32 0) + unreachable +inner.sibling: + %is = catchpad [] + to label %inner.sibling.catch unwind label %inner.end +inner.sibling.catch: + invoke void @f() + to label %unreachable unwind label %inner.end +inner.end: + catchendpad unwind label %right.end +exit: + ret void +} +; This case tests the scenario where two catchpads with the same catchendpad +; have multiple parents. Both %left and %right are parents of %inner and +; %inner.sibling so both of the inner funclets must be cloned. Because +; the catchendpad in %inner.end unwinds to the catchendpad for %right, the +; unwind edge should be removed for the copy of %inner.end that is reached +; from %left. In addition, the %inner.siblin.catch block contains an invoke +; that unwinds to the shared inner catchendpad. The unwind destination for +; this invoke should be updated to unwind to the correct cloned %inner.end +; for each path to the funclet. +; CHECK-LABEL: define void @test17( +; CHECK: left: +; CHECK: %l = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: catchpad [] +; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]] +; CHECK: [[RIGHT_CATCH]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_RIGHT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_SIBLING_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_LEFT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_SIBLING_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: call void @h(i32 0) +; CHECK: unreachable +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: call void @h(i32 0) +; CHECK: unreachable +; CHECK: [[INNER_SIBLING_RIGHT]]: +; CHECK: [[IS_RIGHT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_SIBLING_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_SIBLING_LEFT]]: +; CHECK: [[IS_LEFT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_SIBLING_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_SIBLING_CATCH_RIGHT]]: +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_END_RIGHT]] +; CHECK: [[INNER_SIBLING_CATCH_LEFT]]: +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_END_LEFT]] +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind label %[[RIGHT_END]] + + +define void @test18() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %exit unwind label %right +left: + %l = cleanuppad [] + br label %shared +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + invoke void @f() + to label %unreachable unwind label %inner +unreachable: + unreachable +inner: + %i = catchpad [] + to label %inner.catch unwind label %inner.sibling +inner.catch: + invoke void @f() + to label %unreachable unwind label %inner.end +inner.sibling: + %is = catchpad [] + to label %inner.sibling.catch unwind label %inner.end +inner.sibling.catch: + call void @h(i32 0) + unreachable +inner.end: + catchendpad unwind label %right.end +exit: + ret void +} +; This is like test17 except that the inner invoke is moved from the +; %inner.sibling funclet to %inner so that it is unwinding to a +; catchendpad block that has not yet been cloned. The unwind destination +; of the invoke should still be updated to reach the correct copy of +; %inner.end for the path by which it is reached. +; CHECK-LABEL: define void @test18( +; CHECK: left: +; CHECK: %l = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: catchpad [] +; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]] +; CHECK: [[RIGHT_CATCH]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_RIGHT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_SIBLING_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_LEFT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_SIBLING_LEFT:.+]] +; CHECK: [[INNER_CATCH_RIGHT]]: +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_CATCH_LEFT]]: +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_SIBLING_RIGHT]]: +; CHECK: [[IS_RIGHT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_SIBLING_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT]] +; CHECK: [[INNER_SIBLING_LEFT]]: +; CHECK: [[IS_LEFT:\%.+]] = catchpad [] +; CHECK: to label %[[INNER_SIBLING_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT]] +; CHECK: [[INNER_SIBLING_CATCH_RIGHT]]: +; CHECK: call void @h(i32 0) +; CHECK: unreachable +; CHECK: [[INNER_SIBLING_CATCH_LEFT]]: +; CHECK: call void @h(i32 0) +; CHECK: unreachable +; CHECK: [[INNER_END_LEFT]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind label %[[RIGHT_END]] + + +define void @test19() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %exit unwind label %right +left: + %l = cleanuppad [] + br label %shared +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + invoke void @f() + to label %unreachable unwind label %inner +unreachable: + unreachable +inner: + %i = cleanuppad [] + invoke void @f() + to label %unreachable unwind label %inner.end +inner.end: + cleanupendpad %i unwind label %right.end +exit: + ret void +} +; This case tests the scenario where an invoke in a funclet with multiple +; parents unwinds to a cleanup end pad for the funclet. The unwind destination +; for the invoke should map to the correct copy of the cleanup end pad block. +; CHECK-LABEL: define void @test19( +; CHECK: left: +; CHECK: %l = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: catchpad [] +; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]] +; CHECK: [[RIGHT_CATCH]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_RIGHT:\%.+]] = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_LEFT:\%.+]] = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: cleanupendpad [[I_RIGHT]] unwind label %[[RIGHT_END]] +; CHECK: [[INNER_END_LEFT]]: +; CHECK: cleanupendpad [[I_LEFT]] unwind to caller + +define void @test20() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f() + to label %invoke.cont unwind label %left +invoke.cont: + invoke void @f() + to label %exit unwind label %right +left: + %l = cleanuppad [] + br label %shared +right: + catchpad [] + to label %right.catch unwind label %right.end +right.catch: + br label %shared +right.end: + catchendpad unwind to caller +shared: + invoke void @f() + to label %unreachable unwind label %inner +unreachable: + unreachable +inner: + %i = cleanuppad [] + invoke void @f() + to label %unreachable unwind label %inner.cleanup +inner.cleanup: + cleanuppad [] + call void @f() + unreachable +exit: + ret void +} +; This tests the case where a funclet with multiple parents contains an invoke +; instruction that unwinds to a child funclet. Here %left and %right are both +; parents of %inner. Initially %inner is the only parent of %inner.cleanup but +; after %inner is cloned, %inner.cleanup has multiple parents and so it must +; also be cloned. +; CHECK-LABEL: define void @test20( +; CHECK: left: +; CHECK: %l = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: catchpad [] +; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]] +; CHECK: [[RIGHT_CATCH]]: +; CHECK: invoke void @f() +; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: [[RIGHT_END]]: +; CHECK: catchendpad unwind to caller +; CHECK: [[SHARED_CONT_RIGHT]]: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_RIGHT:\%.+]] = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_CLEANUP_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_LEFT:\%.+]] = cleanuppad [] +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_CLEANUP_LEFT:.+]] +; CHECK: [[INNER_CLEANUP_RIGHT]]: +; CHECK: cleanuppad [] +; CHECK: call void @f() +; CHECK: unreachable +; CHECK: [[INNER_CLEANUP_LEFT]]: +; CHECK: cleanuppad [] +; CHECK: call void @f() +; CHECK: unreachable + + Index: test/CodeGen/WinEH/wineh-no-demotion.ll =================================================================== --- test/CodeGen/WinEH/wineh-no-demotion.ll +++ test/CodeGen/WinEH/wineh-no-demotion.ll @@ -39,12 +39,20 @@ unreachable inner: - ; CHECK: %phi = phi i32 [ %x, %right ], [ 0, %invoke.cont2 ], [ %x.for.left, %left ] %phi = phi i32 [ %x, %shared ], [ 0, %invoke.cont2 ] %i = cleanuppad [] call void @h(i32 %phi) unreachable +; CHECK [[INNER_INVOKE_CONT2:inner.*]]: + ; CHECK: call void @h(i32 0) + +; CHECK [[INNER_RIGHT:inner.*]]: + ; CHECK: call void @h(i32 %x) + +; CHECK [[INNER_LEFT:inner.*]]: + ; CHECK: call void @h(i32 %x.for.left) + exit: unreachable } @@ -76,12 +84,16 @@ unreachable inner: - ; CHECK: %x1 = phi i32 [ %x.for.left, %left ], [ %x, %right ] - ; CHECK: call void @h(i32 %x1) %i = cleanuppad [] call void @h(i32 %x) unreachable +; CHECK [[INNER_RIGHT:inner.*]]: + ; CHECK: call void @h(i32 %x) + +; CHECK [[INNER_LEFT:inner.*]]: + ; CHECK: call void @h(i32 %x.for.left) + exit: unreachable }