[llvm] r252508 - [WinEH] Re-committing r252249 (Clone funclets with multiple parents) with additional fixes for determinism problems
Andrew Kaylor via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 9 11:59:03 PST 2015
Author: akaylor
Date: Mon Nov 9 13:59:02 2015
New Revision: 252508
URL: http://llvm.org/viewvc/llvm-project?rev=252508&view=rev
Log:
[WinEH] Re-committing r252249 (Clone funclets with multiple parents) with additional fixes for determinism problems
Differential Revision: http://reviews.llvm.org/D14454
Added:
llvm/trunk/test/CodeGen/WinEH/wineh-multi-parent-cloning.ll
Modified:
llvm/trunk/lib/CodeGen/WinEHPrepare.cpp
llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll
llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll
llvm/trunk/test/CodeGen/WinEH/wineh-no-demotion.ll
Modified: llvm/trunk/lib/CodeGen/WinEHPrepare.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/WinEHPrepare.cpp?rev=252508&r1=252507&r2=252508&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/WinEHPrepare.cpp (original)
+++ llvm/trunk/lib/CodeGen/WinEHPrepare.cpp Mon Nov 9 13:59:02 2015
@@ -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 @@ static cl::opt<bool> DisableCleanups(
cl::init(false));
namespace {
-
+
class WinEHPrepare : public FunctionPass {
public:
static char ID; // Pass identification, replacement for typeid.
@@ -68,12 +69,26 @@ private:
AllocaInst *insertPHILoads(PHINode *PN, Function &F);
void replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot,
DenseMap<BasicBlock *, Value *> &Loads, Function &F);
- void demoteNonlocalUses(Value *V, std::set<BasicBlock *> &ColorsForBB,
+ void demoteNonlocalUses(Value *V, SetVector<BasicBlock *> &ColorsForBB,
Function &F);
bool prepareExplicitEH(Function &F,
SmallVectorImpl<BasicBlock *> &EntryBlocks);
void replaceTerminatePadWithCleanup(Function &F);
void colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks);
+ void resolveFuncletAncestry(Function &F,
+ SmallVectorImpl<BasicBlock *> &EntryBlocks);
+ void resolveFuncletAncestryForPath(
+ Function &F, SmallVectorImpl<BasicBlock *> &FuncletPath,
+ std::map<BasicBlock *, BasicBlock *> &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<BasicBlock *, BasicBlock *> &Orig2Clone);
+
void demotePHIsOnFunclets(Function &F);
void demoteUsesBetweenFunclets(Function &F);
void demoteArgumentUses(Function &F);
@@ -86,9 +101,20 @@ private:
// All fields are reset by runOnFunction.
EHPersonality Personality = EHPersonality::Unknown;
- std::map<BasicBlock *, std::set<BasicBlock *>> BlockColors;
+ std::map<BasicBlock *, SetVector<BasicBlock *>> BlockColors;
std::map<BasicBlock *, std::set<BasicBlock *>> FuncletBlocks;
- std::map<BasicBlock *, std::set<BasicBlock *>> FuncletChildren;
+ std::map<BasicBlock *, std::vector<BasicBlock *>> FuncletChildren;
+ std::map<BasicBlock *, std::vector<BasicBlock *>> 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<BasicBlock *, std::map<BasicBlock *, BasicBlock*>> EstrangedBlocks;
};
} // end anonymous namespace
@@ -558,9 +584,8 @@ void WinEHPrepare::replaceTerminatePadWi
static void
colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks,
- std::map<BasicBlock *, std::set<BasicBlock *>> &BlockColors,
- std::map<BasicBlock *, std::set<BasicBlock *>> &FuncletBlocks,
- std::map<BasicBlock *, std::set<BasicBlock *>> &FuncletChildren) {
+ std::map<BasicBlock *, SetVector<BasicBlock *>> &BlockColors,
+ std::map<BasicBlock *, std::set<BasicBlock *>> &FuncletBlocks) {
SmallVector<std::pair<BasicBlock *, BasicBlock *>, 16> Worklist;
BasicBlock *EntryBlock = &F.getEntryBlock();
@@ -577,12 +602,18 @@ colorFunclets(Function &F, SmallVectorIm
// 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<CatchEndPadInst>(VisitingHead) &&
!isa<CleanupEndPadInst>(VisitingHead)) {
@@ -600,8 +631,13 @@ colorFunclets(Function &F, SmallVectorIm
if (auto *Exit = dyn_cast<TerminatorInst>(U)) {
for (BasicBlock *Succ : successors(Exit->getParent()))
if (!isa<CatchEndPadInst>(*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 @@ colorFunclets(Function &F, SmallVectorIm
// 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 @@ colorFunclets(Function &F, SmallVectorIm
// 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<CatchPadInst>(UnwindTerminator)) {
+ NextUnwindDest = NextCatch->getUnwindDest();
+ UnwindTerminator = NextUnwindDest->getTerminator();
+ }
+ // The last catch in the chain must unwind to a catchendpad.
+ assert(isa<CatchEndPadInst>(UnwindTerminator));
+ return NextUnwindDest;
+}
+
+static void updateClonedEHPadUnwindToParent(
+ BasicBlock *UnwindDest, BasicBlock *OrigBlock, BasicBlock *CloneBlock,
+ std::vector<BasicBlock *> &OrigParents, BasicBlock *CloneParent) {
+ auto updateUnwindTerminator = [](BasicBlock *BB) {
+ auto *Terminator = BB->getTerminator();
+ if (isa<CatchEndPadInst>(Terminator) ||
+ isa<CleanupEndPadInst>(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<CleanupEndPadInst>(Terminator) ||
+ isa<CleanupReturnInst>(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<CatchEndPadInst>(EHPadInst)) {
+ auto *CloneParentCatch =
+ dyn_cast<CatchPadInst>(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<CatchPadInst>(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<CleanupEndPadInst>(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<CleanupPadInst>(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<CatchEndPadInst>(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<CatchEndPadInst>(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<CatchPadInst>(EHPadInst) || isa<CleanupPadInst>(EHPadInst) ||
+ isa<TerminatePadInst>(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<BasicBlock *> &OrigParents, BasicBlock *CloneParent,
+ ValueToValueMapTy &VMap,
+ std::map<BasicBlock *, SetVector<BasicBlock *>> &BlockColors,
+ std::map<BasicBlock *, std::set<BasicBlock *>> &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<CatchEndPadInst>(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<CatchEndPadInst>(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<BasicBlock *, BasicBlock *> &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<CatchPadInst>(CloneTerminator)) {
+ // A cloned catch pad has a lot of wrinkles, so we'll call a helper function
+ // to update this case.
+ auto *OrigCatch = cast<CatchPadInst>(OrigBlock->getTerminator());
+ updateCatchTerminators(F, OrigCatch, CloneCatch,
+ FuncletParents[OrigFunclet], CloneParent, VMap,
+ BlockColors, FuncletBlocks);
+ } else if (auto *CRI = dyn_cast<CatchReturnInst>(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<CatchReturnInst>(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<CleanupReturnInst>(CloneTerminator) ||
+ isa<CleanupEndPadInst>(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<CleanupReturnInst>(CloneTerminator))
+ UnwindDest = CRI->getUnwindDest();
+ else if (auto *CEI = dyn_cast<CleanupEndPadInst>(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<BasicBlock *> &BlocksInFunclet = FuncletBlocks[FuncletEntry];
+
+ DEBUG_WITH_TYPE("winehprepare-coloring",
+ dbgs() << "Cloning funclet \'" << FuncletEntry->getName()
+ << "\' for parent \'" << Parent->getName() << "\'.\n");
+
+ std::map<BasicBlock *, BasicBlock *> 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<BasicBlock *> &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<PHINode>(&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<Instruction>(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<BasicBlock *> &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<TerminatorInst>(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<PHINode *> PHIsToErase;
+ for (Instruction &I : *ClonedFunclet) {
+ auto *PN = dyn_cast<PHINode>(&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<InvokeInst>(Terminator))
+ UnwindDest = I->getUnwindDest();
+ else if (auto *I = dyn_cast<CatchEndPadInst>(Terminator))
+ UnwindDest = I->getUnwindDest();
+ else if (auto *I = dyn_cast<TerminatePadInst>(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<BasicBlock *> &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<BasicBlock *, SetVector<BasicBlock *>> &BlockColors,
+ std::map<BasicBlock *, std::set<BasicBlock *>> &FuncletBlocks,
+ std::map<BasicBlock *, std::vector<BasicBlock *>> &FuncletParents,
+ std::map<BasicBlock *, std::vector<BasicBlock *>> &FuncletChildren,
+ std::map<BasicBlock *, BasicBlock *> &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<CatchPadInst>(Terminator)) {
+ UnwindDest = I->getUnwindDest();
+ // The catchendpad is not a sibling destination. This case should
+ // have been handled in cloneFuncletForParent().
+ if (isa<CatchEndPadInst>(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<CleanupReturnInst>(Terminator))
+ UnwindDest = I->getUnwindDest();
+ else if (auto *I = dyn_cast<CleanupEndPadInst>(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<CleanupPadInst>(EHPad) && !isa<CatchPadInst>(EHPad) &&
+ !isa<TerminatePadInst>(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<CatchPadInst>(Terminator))
+ I->setUnwindDest(TargetSibling);
+ else if (auto *I = dyn_cast<CleanupReturnInst>(Terminator))
+ I->setUnwindDest(TargetSibling);
+ else if (auto *I = dyn_cast<CleanupEndPadInst>(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<InvokeInst>(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<CleanupEndPadInst>(EHPadInst)) {
+ // An invoke that unwinds to a cleanup end pad must be in a cleanup pad.
+ assert(isa<CleanupPadInst>(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<CatchEndPadInst>(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<CatchPadInst>(ChildFunclet->getFirstNonPHI());
+ auto *OrigCatch = cast<CatchPadInst>(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<InvokeInst>(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<CatchEndPadInst>(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<CatchPadInst>(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<BasicBlock *> &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<BasicBlock *, BasicBlock *> Funclet2Orig;
+ for (auto *Funclet : EntryBlocks)
+ Funclet2Orig[Funclet] = Funclet;
+
+ // Start with the entry funclet and walk the funclet parent-child tree.
+ SmallVector<BasicBlock *, 4> 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<BasicBlock *> &FuncletPath,
+ std::map<BasicBlock *, BasicBlock *> &Funclet2Orig) {
+ bool ClonedAnyChildren = false;
+ BasicBlock *CurFunclet = FuncletPath.back();
+ // Copy the children vector because we might changing it.
+ std::vector<BasicBlock *> 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<BasicBlock *> &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 @@ colorFunclets(Function &F, SmallVectorIm
// the funclet itself (no instruction can target a funclet entry except on
// that transitions to the child funclet).
for (BasicBlock *FuncletEntry : EntryBlocks) {
- std::set<BasicBlock *> &ColorMapItem = BlockColors[FuncletEntry];
- for (BasicBlock *Parent : ColorMapItem)
- FuncletChildren[Parent].insert(FuncletEntry);
+ SetVector<BasicBlock *> &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<BasicBlock *> &EntryBlocks) {
- ::colorFunclets(F, EntryBlocks, BlockColors, FuncletBlocks, FuncletChildren);
-}
-
void llvm::calculateCatchReturnSuccessorColors(const Function *Fn,
WinEHFuncInfo &FuncInfo) {
SmallVector<BasicBlock *, 4> EntryBlocks;
@@ -676,12 +1518,20 @@ void llvm::calculateCatchReturnSuccessor
// findFuncletEntryPoints.
findFuncletEntryPoints(const_cast<Function &>(*Fn), EntryBlocks);
- std::map<BasicBlock *, std::set<BasicBlock *>> BlockColors;
+ std::map<BasicBlock *, SetVector<BasicBlock *>> BlockColors;
std::map<BasicBlock *, std::set<BasicBlock *>> FuncletBlocks;
- std::map<BasicBlock *, std::set<BasicBlock *>> FuncletChildren;
// Figure out which basic blocks belong to which funclets.
colorFunclets(const_cast<Function &>(*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 @@ void llvm::calculateCatchReturnSuccessor
if (!CatchReturn)
continue;
BasicBlock *CatchRetSuccessor = CatchReturn->getSuccessor();
- std::set<BasicBlock *> &SuccessorColors = BlockColors[CatchRetSuccessor];
+ SetVector<BasicBlock *> &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 @@ void WinEHPrepare::demoteUsesBetweenFunc
// 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<BasicBlock *> &ColorsForBB = BlockColors[BB];
+ SetVector<BasicBlock *> &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::demoteUsesBetweenFunc
void WinEHPrepare::demoteArgumentUses(Function &F) {
// Also demote function parameters used in funclets.
- std::set<BasicBlock *> &ColorsForEntry = BlockColors[&F.getEntryBlock()];
+ SetVector<BasicBlock *> &ColorsForEntry = BlockColors[&F.getEntryBlock()];
for (Argument &Arg : F.args())
demoteNonlocalUses(&Arg, ColorsForEntry, F);
}
@@ -772,13 +1622,71 @@ void WinEHPrepare::cloneCommonBlocks(
std::map<BasicBlock *, BasicBlock *> Orig2Clone;
ValueToValueMapTy VMap;
- for (BasicBlock *BB : BlocksInFunclet) {
- std::set<BasicBlock *> &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<BasicBlock *> &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<CatchEndPadInst>(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<CatchPadInst>(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 @@ void WinEHPrepare::cloneCommonBlocks(
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<CatchReturnInst>(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 @@ void WinEHPrepare::cloneCommonBlocks(
for (Use &U : OldI->uses()) {
Instruction *UserI = cast<Instruction>(U.getUser());
BasicBlock *UserBB = UserI->getParent();
- std::set<BasicBlock *> &ColorsForUserBB = BlockColors[UserBB];
+ SetVector<BasicBlock *> &ColorsForUserBB = BlockColors[UserBB];
assert(!ColorsForUserBB.empty());
if (ColorsForUserBB.size() > 1 ||
*ColorsForUserBB.begin() != FuncletPadBB)
@@ -994,6 +1946,8 @@ bool WinEHPrepare::prepareExplicitEH(
cloneCommonBlocks(F, EntryBlocks);
+ resolveFuncletAncestry(F, EntryBlocks);
+
if (!DisableCleanups) {
removeImplausibleTerminators(F);
@@ -1005,6 +1959,9 @@ bool WinEHPrepare::prepareExplicitEH(
BlockColors.clear();
FuncletBlocks.clear();
FuncletChildren.clear();
+ FuncletParents.clear();
+ EstrangedBlocks.clear();
+ FuncletCloningRequired = false;
return true;
}
@@ -1099,10 +2056,24 @@ void WinEHPrepare::insertPHIStore(
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<BasicBlock *> &SetA,
+ SetVector<BasicBlock *> &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<BasicBlock *> &ColorsForBB,
+ SetVector<BasicBlock *> &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 @@ void WinEHPrepare::demoteNonlocalUses(Va
// 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<BasicBlock *> &ColorsForUsingBB = BlockColors[UsingBB];
- if (ColorsForUsingBB == ColorsForBB)
+ SetVector<BasicBlock *> &ColorsForUsingBB = BlockColors[UsingBB];
+ if (isBlockColorSetEquivalent(ColorsForUsingBB, ColorsForBB))
continue;
replaceUseWithLoad(V, U, SpillSlot, Loads, F);
@@ -1145,7 +2116,7 @@ void WinEHPrepare::demoteNonlocalUses(Va
BasicBlock *NewBlock = SplitCriticalEdge(II, SuccNum);
assert(NewBlock && "Unable to split critical edge.");
// Update the color mapping for the newly split edge.
- std::set<BasicBlock *> &ColorsForUsingBB = BlockColors[II->getParent()];
+ SetVector<BasicBlock *> &ColorsForUsingBB = BlockColors[II->getParent()];
BlockColors[NewBlock] = ColorsForUsingBB;
for (BasicBlock *FuncletPad : ColorsForUsingBB)
FuncletBlocks[FuncletPad].insert(NewBlock);
@@ -1212,7 +2183,7 @@ void WinEHPrepare::replaceUseWithLoad(Va
Goto->setSuccessor(0, PHIBlock);
CatchRet->setSuccessor(NewBlock);
// Update the color mapping for the newly split edge.
- std::set<BasicBlock *> &ColorsForPHIBlock = BlockColors[PHIBlock];
+ SetVector<BasicBlock *> &ColorsForPHIBlock = BlockColors[PHIBlock];
BlockColors[NewBlock] = ColorsForPHIBlock;
for (BasicBlock *FuncletPad : ColorsForPHIBlock)
FuncletBlocks[FuncletPad].insert(NewBlock);
Modified: llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll?rev=252508&r1=252507&r2=252508&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll (original)
+++ llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll Mon Nov 9 13:59:02 2015
@@ -280,7 +280,30 @@ exit:
; 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 @@ unreachable:
; 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 @@ 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 @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 @@ unreachable:
; 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:
Modified: llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll?rev=252508&r1=252507&r2=252508&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll (original)
+++ llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll Mon Nov 9 13:59:02 2015
@@ -86,18 +86,18 @@ catch.inner:
; 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:
Added: llvm/trunk/test/CodeGen/WinEH/wineh-multi-parent-cloning.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/wineh-multi-parent-cloning.ll?rev=252508&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/wineh-multi-parent-cloning.ll (added)
+++ llvm/trunk/test/CodeGen/WinEH/wineh-multi-parent-cloning.ll Mon Nov 9 13:59:02 2015
@@ -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
+
+
Modified: llvm/trunk/test/CodeGen/WinEH/wineh-no-demotion.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/wineh-no-demotion.ll?rev=252508&r1=252507&r2=252508&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/wineh-no-demotion.ll (original)
+++ llvm/trunk/test/CodeGen/WinEH/wineh-no-demotion.ll Mon Nov 9 13:59:02 2015
@@ -39,12 +39,20 @@ shared.cont:
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 @@ shared.cont:
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
}
More information about the llvm-commits
mailing list