diff options
Diffstat (limited to 'llvm/lib/CodeGen')
| -rw-r--r-- | llvm/lib/CodeGen/WinEHPrepare.cpp | 991 |
1 files changed, 977 insertions, 14 deletions
diff --git a/llvm/lib/CodeGen/WinEHPrepare.cpp b/llvm/lib/CodeGen/WinEHPrepare.cpp index ca69d321f3b..7175ae5aefd 100644 --- a/llvm/lib/CodeGen/WinEHPrepare.cpp +++ b/llvm/lib/CodeGen/WinEHPrepare.cpp @@ -74,6 +74,20 @@ private: 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); @@ -88,7 +102,18 @@ private: std::map<BasicBlock *, std::set<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 @@ -559,8 +584,7 @@ void WinEHPrepare::replaceTerminatePadWithCleanup(Function &F) { 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 *, std::set<BasicBlock *>> &FuncletBlocks) { SmallVector<std::pair<BasicBlock *, BasicBlock *>, 16> Worklist; BasicBlock *EntryBlock = &F.getEntryBlock(); @@ -577,12 +601,18 @@ colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks, // 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 +630,13 @@ colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks, 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).second) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" + << Color->getName() << "\' to block \'" + << Succ->getName() << "\'.\n"); Worklist.push_back({Succ, Color}); + } } } // Handle CatchPad specially since its successors need different colors. @@ -610,10 +645,18 @@ colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks, // visit the unwind successor with the color of the parent. BasicBlock *NormalSucc = CatchPad->getNormalDest(); if (BlockColors[NormalSucc].insert(Visiting).second) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << Visiting->getName() + << "\' to block \'" << NormalSucc->getName() + << "\'.\n"); Worklist.push_back({NormalSucc, Visiting}); } BasicBlock *UnwindSucc = CatchPad->getUnwindDest(); if (BlockColors[UnwindSucc].insert(Color).second) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << Color->getName() + << "\' to block \'" << UnwindSucc->getName() + << "\'.\n"); Worklist.push_back({UnwindSucc, Color}); } continue; @@ -645,10 +688,806 @@ colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks, continue; } if (BlockColors[Succ].insert(Color).second) { + DEBUG_WITH_TYPE("winehprepare-coloring", + dbgs() << " Assigned color \'" << Color->getName() + << "\' to block \'" << Succ->getName() + << "\'.\n"); Worklist.push_back({Succ, Color}); } } } +} + +static 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 (auto *CEP = dyn_cast<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 *, std::set<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, 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). +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); + } else if (auto *II = dyn_cast<InvokeInst>(CloneTerminator)) { + 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>(CloneFunclet->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[CloneFunclet].count(UnwindDest) && + "Unwind destination for invoke was not updated during cloning."); + } else if (auto *CEP = dyn_cast<CatchEndPadInst>(EHPadInst)) { + auto *OrigCatch = cast<CatchPadInst>(OrigFunclet->getFirstNonPHI()); + auto *CloneCatch = cast<CatchPadInst>(CloneFunclet->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(CloneCatch->getUnwindDest()); + } else if (CloneCatch->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). + auto *OrigInvoke = cast<InvokeInst>(OrigBlock->getTerminator()); + OrigInvoke->setUnwindDest(OrigCatch->getUnwindDest()); + } else { + // If the invoke unwinds to a catch end pad that is not the unwind + // destination for the original catch pad, it must be the unwind + // destination for a sibling catch end pad. We'll handle that case + // later. + assert((getEndPadForCatch(OrigCatch) == UnwindDest || + getEndPadForCatch(CloneCatch) == UnwindDest) && + "Invoke within catch pad unwinds to an invalid catch end pad."); + } + } + } +} + +// 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. +static void updateSiblingToSiblingUnwind( + BasicBlock *CurFunclet, + std::map<BasicBlock *, std::set<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]) { + bool NeedOrigInvokeRemapping = false; + 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 *II = dyn_cast<InvokeInst>(Terminator)) { + 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 (auto *CEP = dyn_cast<CatchEndPadInst>(EHPadInst)) { + // If the invoke unwind destination is the unwind destination for + // the current child catch pad funclet, there is nothing to be done. + auto *CurCatch = cast<CatchPadInst>(ChildFunclet->getFirstNonPHI()); + if (CurCatch->getUnwindDest() == UnwindDest) + continue; + + // 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. + BasicBlock *CurCatchEnd = getEndPadForCatch(CurCatch); + if (CurCatchEnd == UnwindDest) + NeedOrigInvokeRemapping = true; + else + II->setUnwindDest(CurCatchEnd); + continue; + } + // All other unwind scenarios for the invoke are handled elsewhere. + continue; + } else 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); + } + 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 (OrigCatch->getUnwindDest() == UnwindDest) + continue; + + // 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 not already the + // catch end pad at the end unwind chain from the current catch pad, + // we need to remap this invoke now. + 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 @@ -657,18 +1496,27 @@ colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks, // that transitions to the child funclet). for (BasicBlock *FuncletEntry : EntryBlocks) { std::set<BasicBlock *> &ColorMapItem = BlockColors[FuncletEntry]; - for (BasicBlock *Parent : ColorMapItem) - FuncletChildren[Parent].insert(FuncletEntry); + // It will be rare for funclets to have multiple parents, but if any + // do we need to clone the funclet later to address that. Here we + // set a flag indicating that this case has arisen so that we don't + // have to do a lot of checking later to handle the more common case. + if (ColorMapItem.size() > 1) + FuncletCloningRequired = true; + for (BasicBlock *Parent : ColorMapItem) { + assert(std::find(FuncletChildren[Parent].begin(), + FuncletChildren[Parent].end(), + FuncletEntry) == std::end(FuncletChildren[Parent])); + FuncletChildren[Parent].push_back(FuncletEntry); + assert(std::find(FuncletParents[FuncletEntry].begin(), + FuncletParents[FuncletEntry].end(), + Parent) == std::end(FuncletParents[FuncletEntry])); + FuncletParents[FuncletEntry].push_back(Parent); + } ColorMapItem.clear(); ColorMapItem.insert(FuncletEntry); } } -void WinEHPrepare::colorFunclets(Function &F, - SmallVectorImpl<BasicBlock *> &EntryBlocks) { - ::colorFunclets(F, EntryBlocks, BlockColors, FuncletBlocks, FuncletChildren); -} - void llvm::calculateCatchReturnSuccessorColors(const Function *Fn, WinEHFuncInfo &FuncInfo) { SmallVector<BasicBlock *, 4> EntryBlocks; @@ -678,10 +1526,18 @@ void llvm::calculateCatchReturnSuccessorColors(const Function *Fn, std::map<BasicBlock *, std::set<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. @@ -772,13 +1628,71 @@ void WinEHPrepare::cloneCommonBlocks( std::map<BasicBlock *, BasicBlock *> Orig2Clone; ValueToValueMapTy VMap; - for (BasicBlock *BB : BlocksInFunclet) { + 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++; std::set<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 (auto *CEP = dyn_cast<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].erase(Temp); + } + } + + // 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 +1720,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); + + 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 @@ -994,6 +1952,8 @@ bool WinEHPrepare::prepareExplicitEH( cloneCommonBlocks(F, EntryBlocks); + resolveFuncletAncestry(F, EntryBlocks); + if (!DisableCleanups) { removeImplausibleTerminators(F); @@ -1005,6 +1965,9 @@ bool WinEHPrepare::prepareExplicitEH( BlockColors.clear(); FuncletBlocks.clear(); FuncletChildren.clear(); + FuncletParents.clear(); + EstrangedBlocks.clear(); + FuncletCloningRequired = false; return true; } |

