Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -5324,7 +5324,8 @@ if (Lex.getKind() == lltok::kw_caller) { Lex.Lex(); } else { - return true; + return Error(Lex.getLoc(), + "'to' must be followed by 'caller' in catchendpad"); } } else { if (ParseTypeAndBasicBlock(UnwindBB, PFS)) { Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ lib/CodeGen/WinEHPrepare.cpp @@ -74,6 +74,20 @@ 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); @@ -88,7 +102,12 @@ 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; }; } // end anonymous namespace @@ -559,8 +578,7 @@ static void colorFunclets(Function &F, SmallVectorImpl &EntryBlocks, std::map> &BlockColors, - std::map> &FuncletBlocks, - std::map> &FuncletChildren) { + std::map> &FuncletBlocks) { SmallVector, 16> Worklist; BasicBlock *EntryBlock = &F.getEntryBlock(); @@ -577,12 +595,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 +624,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).second) { + 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. @@ -610,10 +639,18 @@ // visit the unwind successor with the color of the parent. BasicBlock *NormalSucc = CatchPad->getNormalDest(); if (BlockColors[NormalSucc].insert(Visiting).second) { + 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) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << Color->getName() + << "\' to block \'" << UnwindSucc->getName() + << "\'.\n"); Worklist.push_back({UnwindSucc, Color}); } continue; @@ -645,10 +682,637 @@ continue; } if (BlockColors[Succ].insert(Color).second) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << Color->getName() + << "\' to block \'" << Succ->getName() + << "\'.\n"); Worklist.push_back({Succ, Color}); } } } +} + +static void updateClonedEHPadUnwindToParent(BasicBlock *UnwindDest, BasicBlock *OrigBlock, BasicBlock *CloneBlock, BasicBlock *CloneParent) { + auto updateUnwindTerminator = [](BasicBlock *BB) { + auto *Terminator = BB->getTerminator(); + if (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 a catchendpad can unwind and each has + // slightly different rules for whether or not it fits with the given + // location. + auto *EHPadInst = UnwindDest->getFirstNonPHI(); + if (auto *CEP = dyn_cast(EHPadInst)) { + auto *CloneParentCatch = + dyn_cast(CloneParent->getFirstNonPHI()); + if (CloneParentCatch && CloneParentCatch->getUnwindDest() == UnwindDest) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " removing unwind destination of original block \'" + << OrigBlock << "\'.\n"); + updateUnwindTerminator(OrigBlock); + } else { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " removing unwind destination of clone block \'" + << CloneBlock->getName() << "\'.\n"); + updateUnwindTerminator(CloneBlock); + } + } else if (auto *CleanupEnd = dyn_cast(EHPadInst)) { + // If the catchendpad 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 catchendpad 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 catchendpad 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 catchendpad 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 unwinds to its 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 + // generate an unreachable block 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 has the right ancestry. + // FIXME: Find a suitable parent. + 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(), 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 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 unwinds to its 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. +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 we're cloning a catch pad and the cloned catch pad returns to + // a block that in the clone parent, we need to replace the original + // catchret with an unreachable instruction. If the catchret does + // not return to a block in the clone parent, we need to replace the + // cloned catchret with an unreachable instruction. + CatchReturnInst *CRIToRemove; + BasicBlock *BlockToModify; + if (FuncletBlocks[CloneParent].count(CRI->getSuccessor())) { + CRIToRemove = cast(OrigBlock->getTerminator()); + BlockToModify = OrigBlock; + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Removing catchret in original block \'" + << OrigBlock->getName() + << "\' because it returns to clone parent \'" + << CloneParent->getName() << "\'.\n"); + } else { + CRIToRemove = CRI; + BlockToModify = CloneBlock; + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() + << " Removing catchret in clone block \'" + << CloneBlock->getName() + << "\' because it does not return to clone parent \'" + << CloneParent->getName() << "\'.\n"); + } + // Loop over all of the successors, removing the modified block's entry from + // any PHI nodes. + for (succ_iterator SI = succ_begin(BlockToModify), SE = succ_end(BlockToModify); + SI != SE; ++SI) + (*SI)->removePredecessor(BlockToModify); + CRIToRemove->eraseFromParent(); + new UnreachableInst(BlockToModify->getContext(), BlockToModify); + } 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, + 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 *OldBlock = BBMapping.first; + 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); + + // 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 either the + // clone parent block or a child of the cloned parent (and therefore a sibling + // of the block we just cloned) 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(); + else if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + // 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. +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); + } + } +} + +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 @@ -657,18 +1321,27 @@ // that transitions to the child funclet). for (BasicBlock *FuncletEntry : EntryBlocks) { std::set &ColorMapItem = BlockColors[FuncletEntry]; - for (BasicBlock *Parent : ColorMapItem) - FuncletChildren[Parent].insert(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; @@ -681,7 +1354,16 @@ 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. @@ -779,6 +1461,35 @@ 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 the catchpad has multiple parents, we'll clone the catchendpad + // when we clone the catchpad funclet and insert it into the correct + // funclet. + if (auto *CEP = dyn_cast(BB->getFirstNonPHI())) { + BlockColors[BB].clear(); + BasicBlock *CatchParent = nullptr; + // There can only be one catchpad predecessor for a catchendpad. + for (BasicBlock *PredBB : predecessors(BB)) { + if (isa(PredBB->getTerminator())) { + CatchParent = PredBB; + } + } + // There must be one catchpad predecessor for a catchendpad. + assert(CatchParent); + BlockColors[BB].insert(FuncletParents[CatchParent].front()); + 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 +1517,18 @@ 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); + + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Removed color \'" << FuncletPadBB->getName() + << "\' from block \'" << OldBlock->getName() + << "\'.\n"); } // Loop over all of the instructions in this funclet, fixing up operand @@ -994,6 +1715,8 @@ cloneCommonBlocks(F, EntryBlocks); + resolveFuncletAncestry(F, EntryBlocks); + if (!DisableCleanups) { removeImplausibleTerminators(F); @@ -1005,6 +1728,8 @@ BlockColors.clear(); FuncletBlocks.clear(); FuncletChildren.clear(); + FuncletParents.clear(); + FuncletCloningRequired = false; return true; } 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,735 @@ +; 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, while entry and left +; are both parents of right. 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). A clone of %right.end will be made for the path from +; the clone of %inner.end made for the %left funclet, but because %left is +; not a catchpad, this should be removed as an implausible terminator. +; 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_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller + + +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_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller + +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_LEFT]]: +; CHECK: catchendpad unwind label %[[LEFT_END]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind to caller + + +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. +; Note: An extra clone of %right is created during the processing of this +; function, but it becomes unreachable and is eliminated. +; 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_LEFT]]: +; CHECK: catchendpad unwind label %[[INNER_SIBLING_LEFT:.+]] +; CHECK: [[INNER_END_RIGHT]]: +; CHECK: catchendpad unwind label %[[INNER_SIBLING_RIGHT:.+]] +; 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 + 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 }