//===-- WinEHPrepare - Prepare exception handling for code generation ---===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This pass lowers LLVM IR exception handling into something closer to what the // backend wants for functions using a personality function from a runtime // provided by MSVC. Functions with other personality functions are left alone // and may be prepared by other passes. In particular, all supported MSVC // personality functions require cleanup code to be outlined, and the C++ // personality requires catch handler code to be outlined. // //===----------------------------------------------------------------------===// #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" #include "llvm/Pass.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/SSAUpdater.h" using namespace llvm; #define DEBUG_TYPE "winehprepare" static cl::opt DisableDemotion( "disable-demotion", cl::Hidden, cl::desc( "Clone multicolor basic blocks but do not demote cross funclet values"), cl::init(false)); static cl::opt DisableCleanups( "disable-cleanups", cl::Hidden, cl::desc("Do not remove implausible terminators or other similar cleanups"), cl::init(false)); namespace { class WinEHPrepare : public FunctionPass { public: static char ID; // Pass identification, replacement for typeid. WinEHPrepare(const TargetMachine *TM = nullptr) : FunctionPass(ID) {} bool runOnFunction(Function &Fn) override; bool doFinalization(Module &M) override; void getAnalysisUsage(AnalysisUsage &AU) const override; const char *getPassName() const override { return "Windows exception handling preparation"; } private: void insertPHIStores(PHINode *OriginalPHI, AllocaInst *SpillSlot); void insertPHIStore(BasicBlock *PredBlock, Value *PredVal, AllocaInst *SpillSlot, SmallVectorImpl> &Worklist); AllocaInst *insertPHILoads(PHINode *PN, Function &F); void replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot, DenseMap &Loads, Function &F); bool prepareExplicitEH(Function &F, SmallVectorImpl &EntryBlocks); void replaceTerminatePadWithCleanup(Function &F); void colorFunclets(Function &F, SmallVectorImpl &EntryBlocks); void resolveFuncletAncestry(Function &F, SmallVectorImpl &EntryBlocks); void resolveFuncletAncestryForPath( Function &F, SmallVectorImpl &FuncletPath, std::map &IdentityMap); void makeFuncletEdgeUnreachable(BasicBlock *Parent, BasicBlock *Child); BasicBlock *cloneFuncletForParent(Function &F, BasicBlock *FuncletEntry, BasicBlock *Parent); void updateTerminatorsAfterFuncletClone( Function &F, BasicBlock *OrigFunclet, BasicBlock *CloneFunclet, BasicBlock *OrigBlock, BasicBlock *CloneBlock, BasicBlock *CloneParent, ValueToValueMapTy &VMap, std::map &Orig2Clone); void demotePHIsOnFunclets(Function &F); void cloneCommonBlocks(Function &F, SmallVectorImpl &EntryBlocks); void removeImplausibleTerminators(Function &F); void cleanupPreparedFunclets(Function &F); void verifyPreparedFunclets(Function &F); // All fields are reset by runOnFunction. EHPersonality Personality = EHPersonality::Unknown; std::map> BlockColors; std::map> FuncletBlocks; std::map> FuncletChildren; std::map> FuncletParents; // This is a flag that indicates an uncommon situation where we need to // clone funclets has been detected. bool FuncletCloningRequired = false; // When a funclet with multiple parents contains a catchret, the block to // which it returns will be cloned so that there is a copy in each parent // but one of the copies will not be properly linked to the catchret and // in most cases will have no predecessors. This double map allows us // to find these cloned blocks when we clone the child funclet. std::map> EstrangedBlocks; }; } // end anonymous namespace char WinEHPrepare::ID = 0; INITIALIZE_TM_PASS(WinEHPrepare, "winehprepare", "Prepare Windows exceptions", false, false) FunctionPass *llvm::createWinEHPass(const TargetMachine *TM) { return new WinEHPrepare(TM); } static void findFuncletEntryPoints(Function &Fn, SmallVectorImpl &EntryBlocks) { EntryBlocks.push_back(&Fn.getEntryBlock()); for (BasicBlock &BB : Fn) { Instruction *First = BB.getFirstNonPHI(); if (!First->isEHPad()) continue; assert(!isa(First) && "landingpad cannot be used with funclet EH personality"); // Find EH pad blocks that represent funclet start points. if (!isa(First) && !isa(First)) EntryBlocks.push_back(&BB); } } bool WinEHPrepare::runOnFunction(Function &Fn) { if (!Fn.hasPersonalityFn()) return false; // Classify the personality to see what kind of preparation we need. Personality = classifyEHPersonality(Fn.getPersonalityFn()); // Do nothing if this is not a funclet-based personality. if (!isFuncletEHPersonality(Personality)) return false; // Remove unreachable blocks. It is not valuable to assign them a color and // their existence can trick us into thinking values are alive when they are // not. removeUnreachableBlocks(Fn); SmallVector EntryBlocks; findFuncletEntryPoints(Fn, EntryBlocks); return prepareExplicitEH(Fn, EntryBlocks); } bool WinEHPrepare::doFinalization(Module &M) { return false; } void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {} static int addUnwindMapEntry(WinEHFuncInfo &FuncInfo, int ToState, const BasicBlock *BB) { CxxUnwindMapEntry UME; UME.ToState = ToState; UME.Cleanup = BB; FuncInfo.CxxUnwindMap.push_back(UME); return FuncInfo.getLastStateNumber(); } static void addTryBlockMapEntry(WinEHFuncInfo &FuncInfo, int TryLow, int TryHigh, int CatchHigh, ArrayRef Handlers) { WinEHTryBlockMapEntry TBME; TBME.TryLow = TryLow; TBME.TryHigh = TryHigh; TBME.CatchHigh = CatchHigh; assert(TBME.TryLow <= TBME.TryHigh); for (const CatchPadInst *CPI : Handlers) { WinEHHandlerType HT; Constant *TypeInfo = cast(CPI->getArgOperand(0)); if (TypeInfo->isNullValue()) HT.TypeDescriptor = nullptr; else HT.TypeDescriptor = cast(TypeInfo->stripPointerCasts()); HT.Adjectives = cast(CPI->getArgOperand(1))->getZExtValue(); HT.Handler = CPI->getParent(); if (isa(CPI->getArgOperand(2))) HT.CatchObj.Alloca = nullptr; else HT.CatchObj.Alloca = cast(CPI->getArgOperand(2)); TBME.HandlerArray.push_back(HT); } FuncInfo.TryBlockMap.push_back(TBME); } static const CatchPadInst *getSingleCatchPadPredecessor(const BasicBlock *BB) { for (const BasicBlock *PredBlock : predecessors(BB)) if (auto *CPI = dyn_cast(PredBlock->getFirstNonPHI())) return CPI; return nullptr; } /// Find all the catchpads that feed directly into the catchendpad. Frontends /// using this personality should ensure that each catchendpad and catchpad has /// one or zero catchpad predecessors. /// /// The following C++ generates the IR after it: /// try { /// } catch (A) { /// } catch (B) { /// } /// /// IR: /// %catchpad.A /// catchpad [i8* A typeinfo] /// to label %catch.A unwind label %catchpad.B /// %catchpad.B /// catchpad [i8* B typeinfo] /// to label %catch.B unwind label %endcatches /// %endcatches /// catchendblock unwind to caller static void findCatchPadsForCatchEndPad(const BasicBlock *CatchEndBB, SmallVectorImpl &Handlers) { const CatchPadInst *CPI = getSingleCatchPadPredecessor(CatchEndBB); while (CPI) { Handlers.push_back(CPI); CPI = getSingleCatchPadPredecessor(CPI->getParent()); } // We've pushed these back into reverse source order. Reverse them to get // the list back into source order. std::reverse(Handlers.begin(), Handlers.end()); } // Given BB which ends in an unwind edge, return the EHPad that this BB belongs // to. If the unwind edge came from an invoke, return null. static const BasicBlock *getEHPadFromPredecessor(const BasicBlock *BB) { const TerminatorInst *TI = BB->getTerminator(); if (isa(TI)) return nullptr; if (TI->isEHPad()) return BB; return cast(TI)->getCleanupPad()->getParent(); } static void calculateExplicitCXXStateNumbers(WinEHFuncInfo &FuncInfo, const BasicBlock &BB, int ParentState) { assert(BB.isEHPad()); const Instruction *FirstNonPHI = BB.getFirstNonPHI(); // All catchpad instructions will be handled when we process their // respective catchendpad instruction. if (isa(FirstNonPHI)) return; if (isa(FirstNonPHI)) { SmallVector Handlers; findCatchPadsForCatchEndPad(&BB, Handlers); const BasicBlock *FirstTryPad = Handlers.front()->getParent(); int TryLow = addUnwindMapEntry(FuncInfo, ParentState, nullptr); FuncInfo.EHPadStateMap[Handlers.front()] = TryLow; for (const BasicBlock *PredBlock : predecessors(FirstTryPad)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, TryLow); int CatchLow = addUnwindMapEntry(FuncInfo, ParentState, nullptr); // catchpads are separate funclets in C++ EH due to the way rethrow works. // In SEH, they aren't, so no invokes will unwind to the catchendpad. FuncInfo.EHPadStateMap[FirstNonPHI] = CatchLow; int TryHigh = CatchLow - 1; for (const BasicBlock *PredBlock : predecessors(&BB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, CatchLow); int CatchHigh = FuncInfo.getLastStateNumber(); addTryBlockMapEntry(FuncInfo, TryLow, TryHigh, CatchHigh, Handlers); DEBUG(dbgs() << "TryLow[" << FirstTryPad->getName() << "]: " << TryLow << '\n'); DEBUG(dbgs() << "TryHigh[" << FirstTryPad->getName() << "]: " << TryHigh << '\n'); DEBUG(dbgs() << "CatchHigh[" << FirstTryPad->getName() << "]: " << CatchHigh << '\n'); } else if (isa(FirstNonPHI)) { // A cleanup can have multiple exits; don't re-process after the first. if (FuncInfo.EHPadStateMap.count(FirstNonPHI)) return; int CleanupState = addUnwindMapEntry(FuncInfo, ParentState, &BB); FuncInfo.EHPadStateMap[FirstNonPHI] = CleanupState; DEBUG(dbgs() << "Assigning state #" << CleanupState << " to BB " << BB.getName() << '\n'); for (const BasicBlock *PredBlock : predecessors(&BB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, CleanupState); } else if (auto *CEPI = dyn_cast(FirstNonPHI)) { // Propagate ParentState to the cleanuppad in case it doesn't have // any cleanuprets. BasicBlock *CleanupBlock = CEPI->getCleanupPad()->getParent(); calculateExplicitCXXStateNumbers(FuncInfo, *CleanupBlock, ParentState); // Anything unwinding through CleanupEndPadInst is in ParentState. FuncInfo.EHPadStateMap[FirstNonPHI] = ParentState; for (const BasicBlock *PredBlock : predecessors(&BB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, ParentState); } else if (isa(FirstNonPHI)) { report_fatal_error("Not yet implemented!"); } else { llvm_unreachable("unexpected EH Pad!"); } } static int addSEHExcept(WinEHFuncInfo &FuncInfo, int ParentState, const Function *Filter, const BasicBlock *Handler) { SEHUnwindMapEntry Entry; Entry.ToState = ParentState; Entry.IsFinally = false; Entry.Filter = Filter; Entry.Handler = Handler; FuncInfo.SEHUnwindMap.push_back(Entry); return FuncInfo.SEHUnwindMap.size() - 1; } static int addSEHFinally(WinEHFuncInfo &FuncInfo, int ParentState, const BasicBlock *Handler) { SEHUnwindMapEntry Entry; Entry.ToState = ParentState; Entry.IsFinally = true; Entry.Filter = nullptr; Entry.Handler = Handler; FuncInfo.SEHUnwindMap.push_back(Entry); return FuncInfo.SEHUnwindMap.size() - 1; } static void calculateExplicitSEHStateNumbers(WinEHFuncInfo &FuncInfo, const BasicBlock &BB, int ParentState) { assert(BB.isEHPad()); const Instruction *FirstNonPHI = BB.getFirstNonPHI(); // All catchpad instructions will be handled when we process their // respective catchendpad instruction. if (isa(FirstNonPHI)) return; if (isa(FirstNonPHI)) { // Extract the filter function and the __except basic block and create a // state for them. SmallVector Handlers; findCatchPadsForCatchEndPad(&BB, Handlers); assert(Handlers.size() == 1 && "SEH doesn't have multiple handlers per __try"); const CatchPadInst *CPI = Handlers.front(); const BasicBlock *CatchPadBB = CPI->getParent(); const Constant *FilterOrNull = cast(CPI->getArgOperand(0)->stripPointerCasts()); const Function *Filter = dyn_cast(FilterOrNull); assert((Filter || FilterOrNull->isNullValue()) && "unexpected filter value"); int TryState = addSEHExcept(FuncInfo, ParentState, Filter, CatchPadBB); // Everything in the __try block uses TryState as its parent state. FuncInfo.EHPadStateMap[CPI] = TryState; DEBUG(dbgs() << "Assigning state #" << TryState << " to BB " << CatchPadBB->getName() << '\n'); for (const BasicBlock *PredBlock : predecessors(CatchPadBB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, TryState); // Everything in the __except block unwinds to ParentState, just like code // outside the __try. FuncInfo.EHPadStateMap[FirstNonPHI] = ParentState; DEBUG(dbgs() << "Assigning state #" << ParentState << " to BB " << BB.getName() << '\n'); for (const BasicBlock *PredBlock : predecessors(&BB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, ParentState); } else if (isa(FirstNonPHI)) { // A cleanup can have multiple exits; don't re-process after the first. if (FuncInfo.EHPadStateMap.count(FirstNonPHI)) return; int CleanupState = addSEHFinally(FuncInfo, ParentState, &BB); FuncInfo.EHPadStateMap[FirstNonPHI] = CleanupState; DEBUG(dbgs() << "Assigning state #" << CleanupState << " to BB " << BB.getName() << '\n'); for (const BasicBlock *PredBlock : predecessors(&BB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, CleanupState); } else if (auto *CEPI = dyn_cast(FirstNonPHI)) { // Propagate ParentState to the cleanuppad in case it doesn't have // any cleanuprets. BasicBlock *CleanupBlock = CEPI->getCleanupPad()->getParent(); calculateExplicitSEHStateNumbers(FuncInfo, *CleanupBlock, ParentState); // Anything unwinding through CleanupEndPadInst is in ParentState. FuncInfo.EHPadStateMap[FirstNonPHI] = ParentState; DEBUG(dbgs() << "Assigning state #" << ParentState << " to BB " << BB.getName() << '\n'); for (const BasicBlock *PredBlock : predecessors(&BB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, ParentState); } else if (isa(FirstNonPHI)) { report_fatal_error("Not yet implemented!"); } else { llvm_unreachable("unexpected EH Pad!"); } } /// Check if the EH Pad unwinds to caller. Cleanups are a little bit of a /// special case because we have to look at the cleanupret instruction that uses /// the cleanuppad. static bool doesEHPadUnwindToCaller(const Instruction *EHPad) { auto *CPI = dyn_cast(EHPad); if (!CPI) return EHPad->mayThrow(); // This cleanup does not return or unwind, so we say it unwinds to caller. if (CPI->use_empty()) return true; const Instruction *User = CPI->user_back(); if (auto *CRI = dyn_cast(User)) return CRI->unwindsToCaller(); return cast(User)->unwindsToCaller(); } void llvm::calculateSEHStateNumbers(const Function *Fn, WinEHFuncInfo &FuncInfo) { // Don't compute state numbers twice. if (!FuncInfo.SEHUnwindMap.empty()) return; for (const BasicBlock &BB : *Fn) { if (!BB.isEHPad() || !doesEHPadUnwindToCaller(BB.getFirstNonPHI())) continue; calculateExplicitSEHStateNumbers(FuncInfo, BB, -1); } } void llvm::calculateWinCXXEHStateNumbers(const Function *Fn, WinEHFuncInfo &FuncInfo) { // Return if it's already been done. if (!FuncInfo.EHPadStateMap.empty()) return; for (const BasicBlock &BB : *Fn) { if (!BB.isEHPad()) continue; if (BB.isLandingPad()) report_fatal_error("MSVC C++ EH cannot use landingpads"); const Instruction *FirstNonPHI = BB.getFirstNonPHI(); if (!doesEHPadUnwindToCaller(FirstNonPHI)) continue; calculateExplicitCXXStateNumbers(FuncInfo, BB, -1); } } static int addClrEHHandler(WinEHFuncInfo &FuncInfo, int ParentState, ClrHandlerType HandlerType, uint32_t TypeToken, const BasicBlock *Handler) { ClrEHUnwindMapEntry Entry; Entry.Parent = ParentState; Entry.Handler = Handler; Entry.HandlerType = HandlerType; Entry.TypeToken = TypeToken; FuncInfo.ClrEHUnwindMap.push_back(Entry); return FuncInfo.ClrEHUnwindMap.size() - 1; } void llvm::calculateClrEHStateNumbers(const Function *Fn, WinEHFuncInfo &FuncInfo) { // Return if it's already been done. if (!FuncInfo.EHPadStateMap.empty()) return; SmallVector, 8> Worklist; // Each pad needs to be able to refer to its parent, so scan the function // looking for top-level handlers and seed the worklist with them. for (const BasicBlock &BB : *Fn) { if (!BB.isEHPad()) continue; if (BB.isLandingPad()) report_fatal_error("CoreCLR EH cannot use landingpads"); const Instruction *FirstNonPHI = BB.getFirstNonPHI(); if (!doesEHPadUnwindToCaller(FirstNonPHI)) continue; // queue this with sentinel parent state -1 to mean unwind to caller. Worklist.emplace_back(FirstNonPHI, -1); } while (!Worklist.empty()) { const Instruction *Pad; int ParentState; std::tie(Pad, ParentState) = Worklist.pop_back_val(); int PredState; if (const CleanupEndPadInst *EndPad = dyn_cast(Pad)) { FuncInfo.EHPadStateMap[EndPad] = ParentState; // Queue the cleanuppad, in case it doesn't have a cleanupret. Worklist.emplace_back(EndPad->getCleanupPad(), ParentState); // Preds of the endpad should get the parent state. PredState = ParentState; } else if (const CleanupPadInst *Cleanup = dyn_cast(Pad)) { // A cleanup can have multiple exits; don't re-process after the first. if (FuncInfo.EHPadStateMap.count(Pad)) continue; // CoreCLR personality uses arity to distinguish faults from finallies. const BasicBlock *PadBlock = Cleanup->getParent(); ClrHandlerType HandlerType = (Cleanup->getNumOperands() ? ClrHandlerType::Fault : ClrHandlerType::Finally); int NewState = addClrEHHandler(FuncInfo, ParentState, HandlerType, 0, PadBlock); FuncInfo.EHPadStateMap[Cleanup] = NewState; // Propagate the new state to all preds of the cleanup PredState = NewState; } else if (const CatchEndPadInst *EndPad = dyn_cast(Pad)) { FuncInfo.EHPadStateMap[EndPad] = ParentState; // Preds of the endpad should get the parent state. PredState = ParentState; } else if (const CatchPadInst *Catch = dyn_cast(Pad)) { const BasicBlock *PadBlock = Catch->getParent(); uint32_t TypeToken = static_cast( cast(Catch->getArgOperand(0))->getZExtValue()); int NewState = addClrEHHandler(FuncInfo, ParentState, ClrHandlerType::Catch, TypeToken, PadBlock); FuncInfo.EHPadStateMap[Catch] = NewState; // Preds of the catch get its state PredState = NewState; } else { llvm_unreachable("Unexpected EH pad"); } // Queue all predecessors with the given state for (const BasicBlock *Pred : predecessors(Pad->getParent())) { if ((Pred = getEHPadFromPredecessor(Pred))) Worklist.emplace_back(Pred->getFirstNonPHI(), PredState); } } } void WinEHPrepare::replaceTerminatePadWithCleanup(Function &F) { if (Personality != EHPersonality::MSVC_CXX) return; for (BasicBlock &BB : F) { Instruction *First = BB.getFirstNonPHI(); auto *TPI = dyn_cast(First); if (!TPI) continue; if (TPI->getNumArgOperands() != 1) report_fatal_error( "Expected a unary terminatepad for MSVC C++ personalities!"); auto *TerminateFn = dyn_cast(TPI->getArgOperand(0)); if (!TerminateFn) report_fatal_error("Function operand expected in terminatepad for MSVC " "C++ personalities!"); // Insert the cleanuppad instruction. auto *CPI = CleanupPadInst::Create( BB.getContext(), {}, Twine("terminatepad.for.", BB.getName()), &BB); // Insert the call to the terminate instruction. auto *CallTerminate = CallInst::Create(TerminateFn, {}, &BB); CallTerminate->setDoesNotThrow(); CallTerminate->setDoesNotReturn(); CallTerminate->setCallingConv(TerminateFn->getCallingConv()); // Insert a new terminator for the cleanuppad using the same successor as // the terminatepad. CleanupReturnInst::Create(CPI, TPI->getUnwindDest(), &BB); // Let's remove the terminatepad now that we've inserted the new // instructions. TPI->eraseFromParent(); } } static void colorFunclets(Function &F, SmallVectorImpl &EntryBlocks, std::map> &BlockColors, std::map> &FuncletBlocks) { SmallVector, 16> Worklist; BasicBlock *EntryBlock = &F.getEntryBlock(); // Build up the color map, which maps each block to its set of 'colors'. // For any block B, the "colors" of B are the set of funclets F (possibly // including a root "funclet" representing the main function), such that // F will need to directly contain B or a copy of B (where the term "directly // contain" is used to distinguish from being "transitively contained" in // a nested funclet). // Use a CFG walk driven by a worklist of (block, color) pairs. The "color" // sets attached during this processing to a block which is the entry of some // funclet F is actually the set of F's parents -- i.e. the union of colors // of all predecessors of F's entry. For all other blocks, the color sets // are as defined above. A post-pass fixes up the block color map to reflect // the same sense of "color" for funclet entries as for other blocks. DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << "\nColoring funclets for " << F.getName() << "\n"); Worklist.push_back({EntryBlock, EntryBlock}); while (!Worklist.empty()) { BasicBlock *Visiting; BasicBlock *Color; std::tie(Visiting, Color) = Worklist.pop_back_val(); DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << "Visiting " << Visiting->getName() << ", " << Color->getName() << "\n"); Instruction *VisitingHead = Visiting->getFirstNonPHI(); if (VisitingHead->isEHPad() && !isa(VisitingHead) && !isa(VisitingHead)) { // Mark this as a funclet head as a member of itself. FuncletBlocks[Visiting].insert(Visiting); // Queue exits (i.e. successors of rets/endpads) with the parent color. // Skip any exits that are catchendpads, since the parent color must then // represent one of the catches chained to that catchendpad, but the // catchendpad should get the color of the common parent of all its // chained catches (i.e. the grandparent color of the current pad). // We don't need to worry abou catchendpads going unvisited, since the // catches chained to them must have unwind edges to them by which we will // visit them. for (User *U : VisitingHead->users()) { if (auto *Exit = dyn_cast(U)) { for (BasicBlock *Succ : successors(Exit->getParent())) if (!isa(*Succ->getFirstNonPHI())) 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. if (CatchPadInst *CatchPad = dyn_cast(VisitingHead)) { // 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)) { 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)) { DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << " Assigned color \'" << Color->getName() << "\' to block \'" << UnwindSucc->getName() << "\'.\n"); Worklist.push_back({UnwindSucc, Color}); } continue; } // Switch color to the current node, except for terminate pads which // have no bodies and only unwind successors and so need their successors // visited with the color of the parent. if (!isa(VisitingHead)) Color = Visiting; } else { // Note that this is a member of the given color. FuncletBlocks[Color].insert(Visiting); } TerminatorInst *Terminator = Visiting->getTerminator(); if (isa(Terminator) || isa(Terminator) || isa(Terminator)) { // These blocks' successors have already been queued with the parent // color. continue; } for (BasicBlock *Succ : successors(Visiting)) { if (isa(Succ->getFirstNonPHI())) { // The catchendpad needs to be visited with the parent's color, not // the current color. This will happen in the code above that visits // any catchpad unwind successor with the parent color, so we can // safely skip this successor here. continue; } if (BlockColors[Succ].insert(Color)) { DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << " Assigned color \'" << Color->getName() << "\' to block \'" << Succ->getName() << "\'.\n"); Worklist.push_back({Succ, Color}); } } } } static BasicBlock *getEndPadForCatch(CatchPadInst *Catch) { // The catch may have sibling catches. Follow the unwind chain until we get // to the catchendpad. BasicBlock *NextUnwindDest = Catch->getUnwindDest(); auto *UnwindTerminator = NextUnwindDest->getTerminator(); while (auto *NextCatch = dyn_cast(UnwindTerminator)) { NextUnwindDest = NextCatch->getUnwindDest(); UnwindTerminator = NextUnwindDest->getTerminator(); } // The last catch in the chain must unwind to a catchendpad. assert(isa(UnwindTerminator)); return NextUnwindDest; } static void updateClonedEHPadUnwindToParent( BasicBlock *UnwindDest, BasicBlock *OrigBlock, BasicBlock *CloneBlock, std::vector &OrigParents, BasicBlock *CloneParent) { auto updateUnwindTerminator = [](BasicBlock *BB) { auto *Terminator = BB->getTerminator(); if (isa(Terminator) || isa(Terminator)) { removeUnwindEdge(BB); } else { // If the block we're updating has a cleanupendpad or cleanupret // terminator, we just want to replace that terminator with an // unreachable instruction. assert(isa(Terminator) || isa(Terminator)); // Loop over all of the successors, removing the block's entry from any // PHI nodes. for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE; ++SI) (*SI)->removePredecessor(BB); // Remove the terminator and replace it with an unreachable instruction. BB->getTerminator()->eraseFromParent(); new UnreachableInst(BB->getContext(), BB); } }; assert(UnwindDest->isEHPad()); // There are many places to which this EH terminator can unwind and each has // slightly different rules for whether or not it fits with the given // location. auto *EHPadInst = UnwindDest->getFirstNonPHI(); if (isa(EHPadInst)) { auto *CloneParentCatch = dyn_cast(CloneParent->getFirstNonPHI()); if (!CloneParentCatch || getEndPadForCatch(CloneParentCatch) != UnwindDest) { DEBUG_WITH_TYPE( "winehprepare-coloring", dbgs() << " removing unwind destination of clone block \'" << CloneBlock->getName() << "\'.\n"); updateUnwindTerminator(CloneBlock); } // It's possible that the catch end pad is a legal match for both the clone // and the original, so they must be checked separately. If the original // funclet will still have multiple parents after the current clone parent // is removed, we'll leave its unwind terminator until later. assert(OrigParents.size() >= 2); if (OrigParents.size() != 2) return; // If the original funclet will have a single parent after the clone parent // is removed, check that parent's unwind destination. assert(OrigParents.front() == CloneParent || OrigParents.back() == CloneParent); BasicBlock *OrigParent; if (OrigParents.front() == CloneParent) OrigParent = OrigParents.back(); else OrigParent = OrigParents.front(); auto *OrigParentCatch = dyn_cast(OrigParent->getFirstNonPHI()); if (!OrigParentCatch || getEndPadForCatch(OrigParentCatch) != UnwindDest) { DEBUG_WITH_TYPE( "winehprepare-coloring", dbgs() << " removing unwind destination of original block \'" << OrigBlock << "\'.\n"); updateUnwindTerminator(OrigBlock); } } else if (auto *CleanupEnd = dyn_cast(EHPadInst)) { // If the EH terminator unwinds to a cleanupendpad, that cleanupendpad // must be ending a cleanuppad of either our clone parent or one // one of the parents of the original funclet. auto *CloneParentCP = dyn_cast(CloneParent->getFirstNonPHI()); auto *EndedCP = CleanupEnd->getCleanupPad(); if (EndedCP == CloneParentCP) { // If it is ending the cleanuppad of our cloned parent, then we // want to remove the unwind destination of the EH terminator that // we associated with the original funclet. assert(isa(OrigBlock->getFirstNonPHI())); DEBUG_WITH_TYPE( "winehprepare-coloring", dbgs() << " removing unwind destination of original block \'" << OrigBlock->getName() << "\'.\n"); updateUnwindTerminator(OrigBlock); } else { // If it isn't ending the cleanuppad of our clone parent, then we // want to remove the unwind destination of the EH terminator that // associated with our cloned funclet. assert(isa(CloneBlock->getFirstNonPHI())); DEBUG_WITH_TYPE( "winehprepare-coloring", dbgs() << " removing unwind destination of clone block \'" << CloneBlock->getName() << "\'.\n"); updateUnwindTerminator(CloneBlock); } } else { // If the EH terminator unwinds to a catchpad, cleanuppad or // terminatepad that EH pad must be a sibling of the funclet we're // cloning. We'll clone it later and update one of the catchendpad // instrunctions that unwinds to it at that time. assert(isa(EHPadInst) || isa(EHPadInst) || isa(EHPadInst)); } } // If the terminator is a catchpad, we must also clone the catchendpad to which // it unwinds and add this to the clone parent's block list. The catchendpad // unwinds to either its caller, a sibling EH pad, a cleanup end pad in its // parent funclet or a catch end pad in its grandparent funclet (which must be // coupled with the parent funclet). If it has no unwind destination // (i.e. unwind to caller), there is nothing to be done. If the unwind // destination is a sibling EH pad, we will update the terminators later (in // resolveFuncletAncestryForPath). If it unwinds to a cleanup end pad or a // catch end pad and this end pad corresponds to the clone parent, we will // remove the unwind destination in the original catchendpad. If it unwinds to // a cleanup end pad or a catch end pad that does not correspond to the clone // parent, we will remove the unwind destination in the cloned catchendpad. static void updateCatchTerminators( Function &F, CatchPadInst *OrigCatch, CatchPadInst *CloneCatch, std::vector &OrigParents, BasicBlock *CloneParent, ValueToValueMapTy &VMap, std::map> &BlockColors, std::map> &FuncletBlocks) { // If we're cloning a catch pad that unwinds to a catchendpad, we also // need to clone the catchendpad. The coloring algorithm associates // the catchendpad block with the funclet's parent, so we have some work // to do here to figure out whether the original belongs to the clone // parent or one of the original funclets other parents (it might have // more than one at this point). In either case, we might also need to // remove the unwind edge if the catchendpad doesn't unwind to a block // in the right grandparent funclet. Instruction *I = CloneCatch->getUnwindDest()->getFirstNonPHI(); if (auto *CEP = dyn_cast(I)) { assert(BlockColors[CEP->getParent()].size() == 1); BasicBlock *CEPFunclet = *(BlockColors[CEP->getParent()].begin()); BasicBlock *CEPCloneParent = nullptr; CatchPadInst *PredCatch = nullptr; if (CEPFunclet == CloneParent) { // The catchendpad is in the clone parent, so we need to clone it // and associate the clone with the original funclet's parent. If // the original funclet had multiple parents, we'll add it to the // first parent that isn't the clone parent. The logic in // updateClonedEHPadUnwindToParent() will only remove the unwind edge // if there is only one parent other than the clone parent, so we don't // need to verify the ancestry. The catchendpad will eventually be // cloned into the correct parent and all invalid unwind edges will be // removed. for (auto *Parent : OrigParents) { if (Parent != CloneParent) { CEPCloneParent = Parent; break; } } PredCatch = OrigCatch; } else { CEPCloneParent = CloneParent; PredCatch = CloneCatch; } assert(CEPCloneParent && PredCatch); DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << " Cloning catchendpad \'" << CEP->getParent()->getName() << "\' for funclet \'" << CEPCloneParent->getName() << "\'.\n"); BasicBlock *ClonedCEP = CloneBasicBlock( CEP->getParent(), VMap, Twine(".from.", CEPCloneParent->getName())); // Insert the clone immediately after the original to ensure determinism // and to keep the same relative ordering of any funclet's blocks. ClonedCEP->insertInto(&F, CEP->getParent()->getNextNode()); PredCatch->setUnwindDest(ClonedCEP); FuncletBlocks[CEPCloneParent].insert(ClonedCEP); BlockColors[ClonedCEP].insert(CEPCloneParent); DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << " Assigning color \'" << CEPCloneParent->getName() << "\' to block \'" << ClonedCEP->getName() << "\'.\n"); auto *ClonedCEPInst = cast(ClonedCEP->getTerminator()); if (auto *Dest = ClonedCEPInst->getUnwindDest()) updateClonedEHPadUnwindToParent(Dest, OrigCatch->getUnwindDest(), CloneCatch->getUnwindDest(), OrigParents, CloneParent); } } // While we are cloning a funclet because it has multiple parents, we will call // this routine to update the terminators for the original and cloned copies // of each basic block. All blocks in the funclet have been clone by this time. // OrigBlock and CloneBlock will be identical except for their block label. // // If the terminator is a catchpad, we must also clone the catchendpad to which // it unwinds and in most cases update either the original catchendpad or the // clone. See the updateCatchTerminators() helper routine for details. // // If the terminator is a catchret its successor is a block in its parent // funclet. If the instruction returns to a block in the parent for which the // cloned funclet was created, the terminator in the original block must be // replaced by an unreachable instruction. Otherwise the terminator in the // clone block must be replaced by an unreachable instruction. // // If the terminator is a cleanupret or cleanupendpad it either unwinds to // caller or unwinds to a sibling EH pad, a cleanup end pad in its parent // funclet or a catch end pad in its grandparent funclet (which must be // coupled with the parent funclet). If it unwinds to caller there is // nothing to be done. If the unwind destination is a sibling EH pad, we will // update the terminators later (in resolveFuncletAncestryForPath). If it // unwinds to a cleanup end pad or a catch end pad and this end pad corresponds // to the clone parent, we will replace the terminator in the original block // with an unreachable instruction. If it unwinds to a cleanup end pad or a // catch end pad that does not correspond to the clone parent, we will replace // the terminator in the clone block with an unreachable instruction. // // If the terminator is an invoke instruction, we will handle it after all // siblings of the current funclet have been cloned. void WinEHPrepare::updateTerminatorsAfterFuncletClone( Function &F, BasicBlock *OrigFunclet, BasicBlock *CloneFunclet, BasicBlock *OrigBlock, BasicBlock *CloneBlock, BasicBlock *CloneParent, ValueToValueMapTy &VMap, std::map &Orig2Clone) { // If the cloned block doesn't have an exceptional terminator, there is // nothing to be done here. TerminatorInst *CloneTerminator = CloneBlock->getTerminator(); if (!CloneTerminator->isExceptional()) return; if (auto *CloneCatch = dyn_cast(CloneTerminator)) { // A cloned catch pad has a lot of wrinkles, so we'll call a helper function // to update this case. auto *OrigCatch = cast(OrigBlock->getTerminator()); updateCatchTerminators(F, OrigCatch, CloneCatch, FuncletParents[OrigFunclet], CloneParent, VMap, BlockColors, FuncletBlocks); } else if (auto *CRI = dyn_cast(CloneTerminator)) { if (FuncletBlocks[CloneParent].count(CRI->getSuccessor())) { BasicBlock *OrigParent; // The original funclet may have more than two parents, but that's OK. // We just need to remap the original catchret to any of the parents. // All of the parents should have an entry in the EstrangedBlocks map // if any of them do. if (FuncletParents[OrigFunclet].front() == CloneParent) OrigParent = FuncletParents[OrigFunclet].back(); else OrigParent = FuncletParents[OrigFunclet].front(); for (succ_iterator SI = succ_begin(OrigBlock), SE = succ_end(OrigBlock); SI != SE; ++SI) (*SI)->removePredecessor(OrigBlock); BasicBlock *LostBlock = EstrangedBlocks[OrigParent][CRI->getSuccessor()]; auto *OrigCatchRet = cast(OrigBlock->getTerminator()); if (LostBlock) { OrigCatchRet->setSuccessor(LostBlock); } else { OrigCatchRet->eraseFromParent(); new UnreachableInst(OrigBlock->getContext(), OrigBlock); } } else { for (succ_iterator SI = succ_begin(CloneBlock), SE = succ_end(CloneBlock); SI != SE; ++SI) (*SI)->removePredecessor(CloneBlock); BasicBlock *LostBlock = EstrangedBlocks[CloneParent][CRI->getSuccessor()]; if (LostBlock) { CRI->setSuccessor(LostBlock); } else { CRI->eraseFromParent(); new UnreachableInst(CloneBlock->getContext(), CloneBlock); } } } else if (isa(CloneTerminator) || isa(CloneTerminator)) { BasicBlock *UnwindDest = nullptr; // A cleanup pad can unwind through either a cleanupret or a cleanupendpad // but both are handled the same way. if (auto *CRI = dyn_cast(CloneTerminator)) UnwindDest = CRI->getUnwindDest(); else if (auto *CEI = dyn_cast(CloneTerminator)) UnwindDest = CEI->getUnwindDest(); // If the instruction has no local unwind destination, there is nothing // to be done. if (!UnwindDest) return; // The unwind destination may be a sibling EH pad, a catchendpad in // a grandparent funclet (ending a catchpad in the parent) or a cleanup // cleanupendpad in the parent. Call a helper routine to diagnose this // and remove either the clone or original terminator as needed. updateClonedEHPadUnwindToParent(UnwindDest, OrigBlock, CloneBlock, FuncletParents[OrigFunclet], CloneParent); } } // Clones all blocks used by the specified funclet to avoid the funclet having // multiple parent funclets. All terminators in the parent that unwind to the // original funclet are remapped to unwind to the clone. Any terminator in the // original funclet which returned to this parent is converted to an unreachable // instruction. Likewise, any terminator in the cloned funclet which returns to // a parent funclet other than the specified parent is converted to an // unreachable instruction. BasicBlock *WinEHPrepare::cloneFuncletForParent(Function &F, BasicBlock *FuncletEntry, BasicBlock *Parent) { std::set &BlocksInFunclet = FuncletBlocks[FuncletEntry]; DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << "Cloning funclet \'" << FuncletEntry->getName() << "\' for parent \'" << Parent->getName() << "\'.\n"); std::map Orig2Clone; ValueToValueMapTy VMap; for (BasicBlock *BB : BlocksInFunclet) { // Create a new basic block and copy instructions into it. BasicBlock *CBB = CloneBasicBlock(BB, VMap, Twine(".from.", Parent->getName())); // Insert the clone immediately after the original to ensure determinism // and to keep the same relative ordering of any funclet's blocks. CBB->insertInto(&F, BB->getNextNode()); // Add basic block mapping. VMap[BB] = CBB; // Record delta operations that we need to perform to our color mappings. Orig2Clone[BB] = CBB; } // end for (BasicBlock *BB : BlocksInFunclet) BasicBlock *ClonedFunclet = Orig2Clone[FuncletEntry]; assert(ClonedFunclet); // Set the coloring for the blocks we just cloned. std::set &ClonedBlocks = FuncletBlocks[ClonedFunclet]; for (auto &BBMapping : Orig2Clone) { BasicBlock *NewBlock = BBMapping.second; ClonedBlocks.insert(NewBlock); BlockColors[NewBlock].insert(ClonedFunclet); DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << " Assigning color \'" << ClonedFunclet->getName() << "\' to block \'" << NewBlock->getName() << "\'.\n"); // Use the VMap to remap the instructions in this cloned block. for (Instruction &I : *NewBlock) RemapInstruction(&I, VMap, RF_IgnoreMissingEntries); } // All the cloned blocks have to be colored in the loop above before we can // update the terminators because doing so can require checking the color of // other blocks in the cloned funclet. for (auto &BBMapping : Orig2Clone) { BasicBlock *OldBlock = BBMapping.first; BasicBlock *NewBlock = BBMapping.second; // Update the terminator, if necessary, in both the original block and the // cloned so that the original funclet never returns to a block in the // clone parent and the clone funclet never returns to a block in any other // of the original funclet's parents. updateTerminatorsAfterFuncletClone(F, FuncletEntry, ClonedFunclet, OldBlock, NewBlock, Parent, VMap, Orig2Clone); // Check to see if the cloned block successor has PHI nodes. If so, we need // to add entries to the PHI nodes for the cloned block now. for (BasicBlock *SuccBB : successors(NewBlock)) { for (Instruction &SuccI : *SuccBB) { auto *SuccPN = dyn_cast(&SuccI); if (!SuccPN) break; // Ok, we have a PHI node. Figure out what the incoming value was for // the OldBlock. int OldBlockIdx = SuccPN->getBasicBlockIndex(OldBlock); if (OldBlockIdx == -1) break; Value *IV = SuccPN->getIncomingValue(OldBlockIdx); // Remap the value if necessary. if (auto *Inst = dyn_cast(IV)) { ValueToValueMapTy::iterator I = VMap.find(Inst); if (I != VMap.end()) IV = I->second; } SuccPN->addIncoming(IV, NewBlock); } } } // Erase the clone's parent from the original funclet's parent list. std::vector &Parents = FuncletParents[FuncletEntry]; Parents.erase(std::remove(Parents.begin(), Parents.end(), Parent), Parents.end()); // Store the cloned funclet's parent. assert(std::find(FuncletParents[ClonedFunclet].begin(), FuncletParents[ClonedFunclet].end(), Parent) == std::end(FuncletParents[ClonedFunclet])); FuncletParents[ClonedFunclet].push_back(Parent); // Copy any children of the original funclet to the clone. We'll either // clone them too or make that path unreachable when we take the next step // in resolveFuncletAncestryForPath(). for (auto *Child : FuncletChildren[FuncletEntry]) { assert(std::find(FuncletChildren[ClonedFunclet].begin(), FuncletChildren[ClonedFunclet].end(), Child) == std::end(FuncletChildren[ClonedFunclet])); FuncletChildren[ClonedFunclet].push_back(Child); assert(std::find(FuncletParents[Child].begin(), FuncletParents[Child].end(), ClonedFunclet) == std::end(FuncletParents[Child])); FuncletParents[Child].push_back(ClonedFunclet); } // Find any blocks that unwound to the original funclet entry from the // clone parent block and remap them to the clone. for (auto *U : FuncletEntry->users()) { auto *UT = dyn_cast(U); if (!UT) continue; BasicBlock *UBB = UT->getParent(); assert(BlockColors[UBB].size() == 1); BasicBlock *UFunclet = *(BlockColors[UBB].begin()); // Funclets shouldn't be able to loop back on themselves. assert(UFunclet != FuncletEntry); // If this instruction unwinds to the original funclet from the clone // parent, remap the terminator so that it unwinds to the clone instead. // We will perform a similar transformation for siblings after all // the siblings have been cloned. if (UFunclet == Parent) { // We're about to break the path from this block to the uncloned funclet // entry, so remove it as a predeccessor to clean up the PHIs. FuncletEntry->removePredecessor(UBB); TerminatorInst *Terminator = UBB->getTerminator(); RemapInstruction(Terminator, VMap, RF_IgnoreMissingEntries); } } // This asserts a condition that is relied upon inside the loop below, // namely that no predecessors of the original funclet entry block // are also predecessors of the cloned funclet entry block. assert(std::all_of(pred_begin(FuncletEntry), pred_end(FuncletEntry), [&ClonedFunclet](BasicBlock *Pred) { return std::find(pred_begin(ClonedFunclet), pred_end(ClonedFunclet), Pred) == pred_end(ClonedFunclet); })); // Remove any invalid PHI node entries in the cloned funclet.cl std::vector PHIsToErase; for (Instruction &I : *ClonedFunclet) { auto *PN = dyn_cast(&I); if (!PN) break; // Predecessors of the original funclet do not reach the cloned funclet, // but the cloning process assumes they will. Remove them now. for (auto *Pred : predecessors(FuncletEntry)) PN->removeIncomingValue(Pred, false); } for (auto *PN : PHIsToErase) PN->eraseFromParent(); // Replace the original funclet in the parent's children vector with the // cloned funclet. for (auto &It : FuncletChildren[Parent]) { if (It == FuncletEntry) { It = ClonedFunclet; break; } } return ClonedFunclet; } // Removes the unwind edge for any exceptional terminators within the specified // parent funclet that previously unwound to the specified child funclet. void WinEHPrepare::makeFuncletEdgeUnreachable(BasicBlock *Parent, BasicBlock *Child) { for (BasicBlock *BB : FuncletBlocks[Parent]) { TerminatorInst *Terminator = BB->getTerminator(); if (!Terminator->isExceptional()) continue; // Look for terninators that unwind to the child funclet. BasicBlock *UnwindDest = nullptr; if (auto *I = dyn_cast(Terminator)) UnwindDest = I->getUnwindDest(); else if (auto *I = dyn_cast(Terminator)) UnwindDest = I->getUnwindDest(); else if (auto *I = dyn_cast(Terminator)) UnwindDest = I->getUnwindDest(); // cleanupendpad, catchret and cleanupret don't represent a parent-to-child // funclet transition, so we don't need to consider them here. // If the child funclet is the unwind destination, replace the terminator // with an unreachable instruction. if (UnwindDest == Child) removeUnwindEdge(BB); } // The specified parent is no longer a parent of the specified child. std::vector &Children = FuncletChildren[Parent]; Children.erase(std::remove(Children.begin(), Children.end(), Child), Children.end()); } // This routine is called after funclets with multiple parents are cloned for // a specific parent. Here we look for children of the specified funclet that // unwind to other children of that funclet and update the unwind destinations // to ensure that each sibling is connected to the correct clone of the sibling // to which it unwinds. // // If the terminator is an invoke instruction, it unwinds either to a child // EH pad, a cleanup end pad in the current funclet, or a catch end pad in a // parent funclet (which ends either the current catch pad or a sibling // catch pad). If it unwinds to a child EH pad, the child will have multiple // parents after this funclet is cloned and this case will be handled later in // the resolveFuncletAncestryForPath processing. If it unwinds to a // cleanup end pad in the current funclet, the instruction remapping during // the cloning process should have already mapped the unwind destination to // the cloned copy of the cleanup end pad. If it unwinds to a catch end pad // there are two possibilities: either the catch end pad is the unwind // destination for the catch pad we are currently cloning or it is the unwind // destination for a sibling catch pad. If it it the unwind destination of the // catch pad we are cloning, we need to update the cloned invoke instruction // to unwind to the cloned catch end pad. Otherwise, we will handle this // later (in resolveFuncletAncestryForPath). static void updateSiblingToSiblingUnwind( BasicBlock *CurFunclet, std::map> &BlockColors, std::map> &FuncletBlocks, std::map> &FuncletParents, std::map> &FuncletChildren, std::map &Funclet2Orig) { // Remap any bad sibling-to-sibling transitions for funclets that // we just cloned. for (BasicBlock *ChildFunclet : FuncletChildren[CurFunclet]) { for (auto *BB : FuncletBlocks[ChildFunclet]) { TerminatorInst *Terminator = BB->getTerminator(); if (!Terminator->isExceptional()) continue; // See if this terminator has an unwind destination. // Note that catchendpads are handled when the associated catchpad // is cloned. They don't fit the pattern we're looking for here. BasicBlock *UnwindDest = nullptr; if (auto *I = dyn_cast(Terminator)) { UnwindDest = I->getUnwindDest(); // The catchendpad is not a sibling destination. This case should // have been handled in cloneFuncletForParent(). if (isa(Terminator)) { assert(BlockColors[UnwindDest].size() == 1 && "Cloned catchpad unwinds to an pad with multiple parents."); assert(FuncletParents[UnwindDest].front() == CurFunclet && "Cloned catchpad unwinds to the wrong parent."); continue; } } else { if (auto *I = dyn_cast(Terminator)) UnwindDest = I->getUnwindDest(); else if (auto *I = dyn_cast(Terminator)) UnwindDest = I->getUnwindDest(); // If the cleanup unwinds to caller, there is nothing to be done. if (!UnwindDest) continue; } // If the destination is not a cleanup pad, catch pad or terminate pad // we don't need to handle it here. Instruction *EHPad = UnwindDest->getFirstNonPHI(); if (!isa(EHPad) && !isa(EHPad) && !isa(EHPad)) continue; // If it is one of these, then it is either a sibling of the current // child funclet or a clone of one of those siblings. // If it is a sibling, no action is needed. if (FuncletParents[UnwindDest].size() == 1 && FuncletParents[UnwindDest].front() == CurFunclet) continue; // If the unwind destination is a clone of a sibling, we need to figure // out which sibling it is a clone of and use that sibling as the // unwind destination. BasicBlock *DestOrig = Funclet2Orig[UnwindDest]; BasicBlock *TargetSibling = nullptr; for (auto &Mapping : Funclet2Orig) { if (Mapping.second != DestOrig) continue; BasicBlock *MappedFunclet = Mapping.first; if (FuncletParents[MappedFunclet].size() == 1 && FuncletParents[MappedFunclet].front() == CurFunclet) { TargetSibling = MappedFunclet; } } // If we didn't find the sibling we were looking for then the // unwind destination is not a clone of one of child's siblings. // That's unexpected. assert(TargetSibling && "Funclet unwinds to unexpected destination."); // Update the terminator instruction to unwind to the correct sibling. if (auto *I = dyn_cast(Terminator)) I->setUnwindDest(TargetSibling); else if (auto *I = dyn_cast(Terminator)) I->setUnwindDest(TargetSibling); else if (auto *I = dyn_cast(Terminator)) I->setUnwindDest(TargetSibling); } } // Invoke remapping can't be done correctly until after all of their // other sibling-to-sibling unwinds have been remapped. for (BasicBlock *ChildFunclet : FuncletChildren[CurFunclet]) { bool NeedOrigInvokeRemapping = false; for (auto *BB : FuncletBlocks[ChildFunclet]) { TerminatorInst *Terminator = BB->getTerminator(); auto *II = dyn_cast(Terminator); if (!II) continue; BasicBlock *UnwindDest = II->getUnwindDest(); assert(UnwindDest && "Invoke unwinds to a null destination."); assert(UnwindDest->isEHPad() && "Invoke does not unwind to an EH pad."); auto *EHPadInst = UnwindDest->getFirstNonPHI(); if (isa(EHPadInst)) { // An invoke that unwinds to a cleanup end pad must be in a cleanup pad. assert(isa(ChildFunclet->getFirstNonPHI()) && "Unwinding to cleanup end pad from a non cleanup pad funclet."); // The funclet cloning should have remapped the destination to the cloned // cleanup end pad. assert(FuncletBlocks[ChildFunclet].count(UnwindDest) && "Unwind destination for invoke was not updated during cloning."); } else if (isa(EHPadInst)) { // If the invoke unwind destination is the unwind destination for // the current child catch pad funclet, there is nothing to be done. BasicBlock *OrigFunclet = Funclet2Orig[ChildFunclet]; auto *CurCatch = cast(ChildFunclet->getFirstNonPHI()); auto *OrigCatch = cast(OrigFunclet->getFirstNonPHI()); if (OrigCatch->getUnwindDest() == UnwindDest) { // If the invoke unwinds to a catch end pad that is the unwind // destination for the original catch pad, the cloned invoke should // unwind to the cloned catch end pad. II->setUnwindDest(CurCatch->getUnwindDest()); } else if (CurCatch->getUnwindDest() == UnwindDest) { // If the invoke unwinds to a catch end pad that is the unwind // destination for the clone catch pad, the original invoke should // unwind to the unwind destination of the original catch pad. // This happens when the catch end pad is matched to the clone // parent when the catchpad instruction is cloned and the original // invoke instruction unwinds to the original catch end pad (which // is now the unwind destination of the cloned catch pad). NeedOrigInvokeRemapping = true; } else { // Otherwise, the invoke unwinds to a catch end pad that is the unwind // destination another catch pad in the unwind chain from either the // current catch pad or one of its clones. If it is already the // catch end pad at the end unwind chain from the current catch pad, // we'll need to check the invoke instructions in the original funclet // later. Otherwise, we need to remap this invoke now. assert((getEndPadForCatch(OrigCatch) == UnwindDest || getEndPadForCatch(CurCatch) == UnwindDest) && "Invoke within catch pad unwinds to an invalid catch end pad."); BasicBlock *CurCatchEnd = getEndPadForCatch(CurCatch); if (CurCatchEnd == UnwindDest) NeedOrigInvokeRemapping = true; else II->setUnwindDest(CurCatchEnd); } } } if (NeedOrigInvokeRemapping) { // To properly remap invoke instructions that unwind to catch end pads // that are not the unwind destination of the catch pad funclet in which // the invoke appears, we must also look at the uncloned invoke in the // original funclet. If we saw an invoke that was already properly // unwinding to a sibling's catch end pad, we need to check the invokes // in the original funclet. BasicBlock *OrigFunclet = Funclet2Orig[ChildFunclet]; for (auto *BB : FuncletBlocks[OrigFunclet]) { auto *II = dyn_cast(BB->getTerminator()); if (!II) continue; BasicBlock *UnwindDest = II->getUnwindDest(); assert(UnwindDest && "Invoke unwinds to a null destination."); assert(UnwindDest->isEHPad() && "Invoke does not unwind to an EH pad."); auto *CEP = dyn_cast(UnwindDest->getFirstNonPHI()); if (!CEP) continue; // If the invoke unwind destination is the unwind destination for // the original catch pad funclet, there is nothing to be done. auto *OrigCatch = cast(OrigFunclet->getFirstNonPHI()); // If the invoke unwinds to a catch end pad that is neither the unwind // destination of OrigCatch or the destination another catch pad in the // unwind chain from current catch pad, we need to remap the invoke. BasicBlock *OrigCatchEnd = getEndPadForCatch(OrigCatch); if (OrigCatchEnd != UnwindDest) II->setUnwindDest(OrigCatchEnd); } } } } void WinEHPrepare::resolveFuncletAncestry( Function &F, SmallVectorImpl &EntryBlocks) { // Most of the time this will be unnecessary. If the conditions arise that // require this work, this flag will be set. if (!FuncletCloningRequired) return; // Funclet2Orig is used to map any cloned funclets back to the original // funclet from which they were cloned. The map is seeded with the // original funclets mapping to themselves. std::map Funclet2Orig; for (auto *Funclet : EntryBlocks) Funclet2Orig[Funclet] = Funclet; // Start with the entry funclet and walk the funclet parent-child tree. SmallVector FuncletPath; FuncletPath.push_back(&(F.getEntryBlock())); resolveFuncletAncestryForPath(F, FuncletPath, Funclet2Orig); } // Walks the funclet control flow, cloning any funclets that have more than one // parent funclet and breaking any cyclic unwind chains so that the path becomes // unreachable at the point where a funclet would have unwound to a funclet that // was already in the chain. void WinEHPrepare::resolveFuncletAncestryForPath( Function &F, SmallVectorImpl &FuncletPath, std::map &Funclet2Orig) { bool ClonedAnyChildren = false; BasicBlock *CurFunclet = FuncletPath.back(); // Copy the children vector because we might changing it. std::vector Children(FuncletChildren[CurFunclet]); for (BasicBlock *ChildFunclet : Children) { // Don't allow the funclet chain to unwind back on itself. // If this funclet is already in the current funclet chain, make the // path to it through the current funclet unreachable. bool IsCyclic = false; BasicBlock *ChildIdentity = Funclet2Orig[ChildFunclet]; for (BasicBlock *Ancestor : FuncletPath) { BasicBlock *AncestorIdentity = Funclet2Orig[Ancestor]; if (AncestorIdentity == ChildIdentity) { IsCyclic = true; break; } } // If the unwind chain wraps back on itself, break the chain. if (IsCyclic) { makeFuncletEdgeUnreachable(CurFunclet, ChildFunclet); continue; } // If this child funclet has other parents, clone the entire funclet. if (FuncletParents[ChildFunclet].size() > 1) { ChildFunclet = cloneFuncletForParent(F, ChildFunclet, CurFunclet); Funclet2Orig[ChildFunclet] = ChildIdentity; ClonedAnyChildren = true; } FuncletPath.push_back(ChildFunclet); resolveFuncletAncestryForPath(F, FuncletPath, Funclet2Orig); FuncletPath.pop_back(); } // If we didn't clone any children, we can return now. if (!ClonedAnyChildren) return; updateSiblingToSiblingUnwind(CurFunclet, BlockColors, FuncletBlocks, FuncletParents, FuncletChildren, Funclet2Orig); } void WinEHPrepare::colorFunclets(Function &F, SmallVectorImpl &EntryBlocks) { ::colorFunclets(F, EntryBlocks, BlockColors, FuncletBlocks); // The processing above actually accumulated the parent set for this // funclet into the color set for its entry; use the parent set to // populate the children map, and reset the color set to include just // the funclet itself (no instruction can target a funclet entry except on // that transitions to the child funclet). for (BasicBlock *FuncletEntry : EntryBlocks) { SetVector &ColorMapItem = BlockColors[FuncletEntry]; // It will be rare for funclets to have multiple parents, but if any // do we need to clone the funclet later to address that. Here we // set a flag indicating that this case has arisen so that we don't // have to do a lot of checking later to handle the more common case. if (ColorMapItem.size() > 1) FuncletCloningRequired = true; for (BasicBlock *Parent : ColorMapItem) { assert(std::find(FuncletChildren[Parent].begin(), FuncletChildren[Parent].end(), FuncletEntry) == std::end(FuncletChildren[Parent])); FuncletChildren[Parent].push_back(FuncletEntry); assert(std::find(FuncletParents[FuncletEntry].begin(), FuncletParents[FuncletEntry].end(), Parent) == std::end(FuncletParents[FuncletEntry])); FuncletParents[FuncletEntry].push_back(Parent); } ColorMapItem.clear(); ColorMapItem.insert(FuncletEntry); } } void llvm::calculateCatchReturnSuccessorColors(const Function *Fn, WinEHFuncInfo &FuncInfo) { SmallVector EntryBlocks; // colorFunclets needs the set of EntryBlocks, get them using // findFuncletEntryPoints. findFuncletEntryPoints(const_cast(*Fn), EntryBlocks); std::map> BlockColors; std::map> FuncletBlocks; // Figure out which basic blocks belong to which funclets. colorFunclets(const_cast(*Fn), EntryBlocks, BlockColors, 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. for (auto &Funclet : FuncletBlocks) { // Figure out what kind of funclet we are looking at; We only care about // catchpads. BasicBlock *FuncletPadBB = Funclet.first; Instruction *FirstNonPHI = FuncletPadBB->getFirstNonPHI(); auto *CatchPad = dyn_cast(FirstNonPHI); if (!CatchPad) continue; // The users of a catchpad are always catchrets. for (User *Exit : CatchPad->users()) { auto *CatchReturn = dyn_cast(Exit); if (!CatchReturn) continue; BasicBlock *CatchRetSuccessor = CatchReturn->getSuccessor(); SetVector &SuccessorColors = BlockColors[CatchRetSuccessor]; assert(SuccessorColors.size() == 1 && "Expected BB to be monochrome!"); BasicBlock *Color = *SuccessorColors.begin(); // Record the catchret successor's funclet membership. FuncInfo.CatchRetSuccessorColorMap[CatchReturn] = Color; } } } void WinEHPrepare::demotePHIsOnFunclets(Function &F) { // Strip PHI nodes off of EH pads. SmallVector PHINodes; for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) { BasicBlock *BB = &*FI++; if (!BB->isEHPad()) continue; for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) { Instruction *I = &*BI++; auto *PN = dyn_cast(I); // Stop at the first non-PHI. if (!PN) break; AllocaInst *SpillSlot = insertPHILoads(PN, F); if (SpillSlot) insertPHIStores(PN, SpillSlot); PHINodes.push_back(PN); } } for (auto *PN : PHINodes) { // There may be lingering uses on other EH PHIs being removed PN->replaceAllUsesWith(UndefValue::get(PN->getType())); PN->eraseFromParent(); } } void WinEHPrepare::cloneCommonBlocks( Function &F, SmallVectorImpl &EntryBlocks) { // We need to clone all blocks which belong to multiple funclets. Values are // remapped throughout the funclet to propogate both the new instructions // *and* the new basic blocks themselves. for (BasicBlock *FuncletPadBB : EntryBlocks) { std::set &BlocksInFunclet = FuncletBlocks[FuncletPadBB]; std::map Orig2Clone; ValueToValueMapTy VMap; for (auto BlockIt = BlocksInFunclet.begin(), BlockEnd = BlocksInFunclet.end(); BlockIt != BlockEnd;) { // Increment the iterator inside the loop because we might be removing // blocks from the set. BasicBlock *BB = *BlockIt++; SetVector &ColorsForBB = BlockColors[BB]; // We don't need to do anything if the block is monochromatic. size_t NumColorsForBB = ColorsForBB.size(); if (NumColorsForBB == 1) continue; // If this block is a catchendpad, it shouldn't be cloned. // We will only see a catchendpad with multiple colors in the case where // some funclet has multiple parents. In that case, the color will be // resolved during the resolveFuncletAncestry processing. // For now, find the catchpad that unwinds to this block and assign // that catchpad's first parent to be the color for this block. if (isa(BB->getFirstNonPHI())) { assert( FuncletCloningRequired && "Found multi-colored catchendpad with no multi-parent funclets."); BasicBlock *CatchParent = nullptr; // There can only be one catchpad predecessor for a catchendpad. for (BasicBlock *PredBB : predecessors(BB)) { if (isa(PredBB->getTerminator())) { CatchParent = PredBB; break; } } // There must be one catchpad predecessor for a catchendpad. assert(CatchParent && "No catchpad found for catchendpad."); // If the catchpad has multiple parents, we'll clone the catchendpad // when we clone the catchpad funclet and insert it into the correct // funclet. For now, we just select the first parent of the catchpad // and give the catchendpad that color. BasicBlock *CorrectColor = FuncletParents[CatchParent].front(); assert(FuncletBlocks[CorrectColor].count(BB)); assert(BlockColors[BB].count(CorrectColor)); // Remove this block from the FuncletBlocks set of any funclet that // isn't the funclet whose color we just selected. for (BasicBlock *ContainingFunclet : BlockColors[BB]) if (ContainingFunclet != CorrectColor) FuncletBlocks[ContainingFunclet].erase(BB); BlockColors[BB].remove_if([&](BasicBlock *ContainingFunclet) { return ContainingFunclet != CorrectColor; }); // 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())); // 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; } // If nothing was cloned, we're done cloning in this funclet. if (Orig2Clone.empty()) continue; // Update our color mappings to reflect that one block has lost a color and // another has gained a color. for (auto &BBMapping : Orig2Clone) { BasicBlock *OldBlock = BBMapping.first; BasicBlock *NewBlock = BBMapping.second; 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].remove(FuncletPadBB); DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << " Removed color \'" << FuncletPadBB->getName() << "\' from block \'" << OldBlock->getName() << "\'.\n"); // If we are cloning a funclet that might share a child funclet with // another funclet, look to see if the cloned block is reached from a // catchret instruction. If so, save this association so we can retrieve // the possibly orphaned clone when we clone the child funclet. if (FuncletCloningRequired) { for (auto *Pred : predecessors(OldBlock)) { auto *Terminator = Pred->getTerminator(); if (!isa(Terminator)) continue; // If this block is reached from a catchret instruction in a funclet // that has multiple parents, it will have a color for each of those // parents. We just removed the color of one of the parents, but // the cloned block will be unreachable until we clone the child // funclet that contains the catchret instruction. In that case we // need to create a mapping that will let us find the cloned block // later and associate it with the cloned child funclet. bool BlockWillBeEstranged = false; for (auto *Color : BlockColors[Pred]) { if (FuncletParents[Color].size() > 1) { BlockWillBeEstranged = true; break; // Breaks out of the color loop } } if (BlockWillBeEstranged) { EstrangedBlocks[FuncletPadBB][OldBlock] = NewBlock; DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << " Saved mapping of estranged block \'" << NewBlock->getName() << "\' for \'" << FuncletPadBB->getName() << "\'.\n"); break; // Breaks out of the predecessor loop } } } } // Loop over all of the instructions in this funclet, fixing up operand // references as we go. This uses VMap to do all the hard work. for (BasicBlock *BB : BlocksInFunclet) // Loop over all instructions, fixing each one as we find it... for (Instruction &I : *BB) RemapInstruction(&I, VMap, RF_IgnoreMissingEntries | RF_NoModuleLevelChanges); // Check to see if SuccBB has PHI nodes. If so, we need to add entries to // the PHI nodes for NewBB now. for (auto &BBMapping : Orig2Clone) { BasicBlock *OldBlock = BBMapping.first; BasicBlock *NewBlock = BBMapping.second; for (BasicBlock *SuccBB : successors(NewBlock)) { for (Instruction &SuccI : *SuccBB) { auto *SuccPN = dyn_cast(&SuccI); if (!SuccPN) break; // Ok, we have a PHI node. Figure out what the incoming value was for // the OldBlock. int OldBlockIdx = SuccPN->getBasicBlockIndex(OldBlock); if (OldBlockIdx == -1) break; Value *IV = SuccPN->getIncomingValue(OldBlockIdx); // Remap the value if necessary. if (auto *Inst = dyn_cast(IV)) { ValueToValueMapTy::iterator I = VMap.find(Inst); if (I != VMap.end()) IV = I->second; } SuccPN->addIncoming(IV, NewBlock); } } } for (ValueToValueMapTy::value_type VT : VMap) { // If there were values defined in BB that are used outside the funclet, // then we now have to update all uses of the value to use either the // original value, the cloned value, or some PHI derived value. This can // require arbitrary PHI insertion, of which we are prepared to do, clean // these up now. SmallVector UsesToRename; auto *OldI = dyn_cast(const_cast(VT.first)); if (!OldI) continue; auto *NewI = cast(VT.second); // Scan all uses of this instruction to see if it is used outside of its // funclet, and if so, record them in UsesToRename. for (Use &U : OldI->uses()) { Instruction *UserI = cast(U.getUser()); BasicBlock *UserBB = UserI->getParent(); SetVector &ColorsForUserBB = BlockColors[UserBB]; assert(!ColorsForUserBB.empty()); if (ColorsForUserBB.size() > 1 || *ColorsForUserBB.begin() != FuncletPadBB) UsesToRename.push_back(&U); } // If there are no uses outside the block, we're done with this // instruction. if (UsesToRename.empty()) continue; // We found a use of OldI outside of the funclet. Rename all uses of OldI // that are outside its funclet to be uses of the appropriate PHI node // etc. SSAUpdater SSAUpdate; SSAUpdate.Initialize(OldI->getType(), OldI->getName()); SSAUpdate.AddAvailableValue(OldI->getParent(), OldI); SSAUpdate.AddAvailableValue(NewI->getParent(), NewI); while (!UsesToRename.empty()) SSAUpdate.RewriteUseAfterInsertions(*UsesToRename.pop_back_val()); } } } void WinEHPrepare::removeImplausibleTerminators(Function &F) { // Remove implausible terminators and replace them with UnreachableInst. for (auto &Funclet : FuncletBlocks) { BasicBlock *FuncletPadBB = Funclet.first; std::set &BlocksInFunclet = Funclet.second; Instruction *FirstNonPHI = FuncletPadBB->getFirstNonPHI(); auto *CatchPad = dyn_cast(FirstNonPHI); auto *CleanupPad = dyn_cast(FirstNonPHI); for (BasicBlock *BB : BlocksInFunclet) { TerminatorInst *TI = BB->getTerminator(); // CatchPadInst and CleanupPadInst can't transfer control to a ReturnInst. bool IsUnreachableRet = isa(TI) && (CatchPad || CleanupPad); // The token consumed by a CatchReturnInst must match the funclet token. bool IsUnreachableCatchret = false; if (auto *CRI = dyn_cast(TI)) IsUnreachableCatchret = CRI->getCatchPad() != CatchPad; // The token consumed by a CleanupReturnInst must match the funclet token. bool IsUnreachableCleanupret = false; if (auto *CRI = dyn_cast(TI)) IsUnreachableCleanupret = CRI->getCleanupPad() != CleanupPad; // The token consumed by a CleanupEndPadInst must match the funclet token. bool IsUnreachableCleanupendpad = false; if (auto *CEPI = dyn_cast(TI)) IsUnreachableCleanupendpad = CEPI->getCleanupPad() != CleanupPad; if (IsUnreachableRet || IsUnreachableCatchret || IsUnreachableCleanupret || IsUnreachableCleanupendpad) { // Loop through all of our successors and make sure they know that one // of their predecessors is going away. for (BasicBlock *SuccBB : TI->successors()) SuccBB->removePredecessor(BB); if (IsUnreachableCleanupendpad) { // We can't simply replace a cleanupendpad with unreachable, because // its predecessor edges are EH edges and unreachable is not an EH // pad. Change all predecessors to the "unwind to caller" form. for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE;) { BasicBlock *Pred = *PI++; removeUnwindEdge(Pred); } } new UnreachableInst(BB->getContext(), TI); TI->eraseFromParent(); } // FIXME: Check for invokes/cleanuprets/cleanupendpads which unwind to // implausible catchendpads (i.e. catchendpad not in immediate parent // funclet). } } } void WinEHPrepare::cleanupPreparedFunclets(Function &F) { // Clean-up some of the mess we made by removing useles PHI nodes, trivial // branches, etc. for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) { BasicBlock *BB = &*FI++; SimplifyInstructionsInBlock(BB); ConstantFoldTerminator(BB, /*DeleteDeadConditions=*/true); MergeBlockIntoPredecessor(BB); } // We might have some unreachable blocks after cleaning up some impossible // control flow. removeUnreachableBlocks(F); } void WinEHPrepare::verifyPreparedFunclets(Function &F) { // Recolor the CFG to verify that all is well. for (BasicBlock &BB : F) { size_t NumColors = BlockColors[&BB].size(); assert(NumColors == 1 && "Expected monochromatic BB!"); if (NumColors == 0) report_fatal_error("Uncolored BB!"); if (NumColors > 1) report_fatal_error("Multicolor BB!"); bool EHPadHasPHI = BB.isEHPad() && isa(BB.begin()); assert(!EHPadHasPHI && "EH Pad still has a PHI!"); if (EHPadHasPHI) report_fatal_error("EH Pad still has a PHI!"); } } bool WinEHPrepare::prepareExplicitEH( Function &F, SmallVectorImpl &EntryBlocks) { replaceTerminatePadWithCleanup(F); // Determine which blocks are reachable from which funclet entries. colorFunclets(F, EntryBlocks); if (!DisableDemotion) demotePHIsOnFunclets(F); cloneCommonBlocks(F, EntryBlocks); resolveFuncletAncestry(F, EntryBlocks); if (!DisableCleanups) { removeImplausibleTerminators(F); cleanupPreparedFunclets(F); } verifyPreparedFunclets(F); BlockColors.clear(); FuncletBlocks.clear(); FuncletChildren.clear(); FuncletParents.clear(); EstrangedBlocks.clear(); FuncletCloningRequired = false; return true; } // TODO: Share loads when one use dominates another, or when a catchpad exit // dominates uses (needs dominators). AllocaInst *WinEHPrepare::insertPHILoads(PHINode *PN, Function &F) { BasicBlock *PHIBlock = PN->getParent(); AllocaInst *SpillSlot = nullptr; if (isa(PHIBlock->getFirstNonPHI())) { // Insert a load in place of the PHI and replace all uses. SpillSlot = new AllocaInst(PN->getType(), nullptr, Twine(PN->getName(), ".wineh.spillslot"), &F.getEntryBlock().front()); Value *V = new LoadInst(SpillSlot, Twine(PN->getName(), ".wineh.reload"), &*PHIBlock->getFirstInsertionPt()); PN->replaceAllUsesWith(V); return SpillSlot; } DenseMap Loads; for (Value::use_iterator UI = PN->use_begin(), UE = PN->use_end(); UI != UE;) { Use &U = *UI++; auto *UsingInst = cast(U.getUser()); BasicBlock *UsingBB = UsingInst->getParent(); if (UsingBB->isEHPad()) { // Use is on an EH pad phi. Leave it alone; we'll insert loads and // stores for it separately. assert(isa(UsingInst)); continue; } replaceUseWithLoad(PN, U, SpillSlot, Loads, F); } return SpillSlot; } // TODO: improve store placement. Inserting at def is probably good, but need // to be careful not to introduce interfering stores (needs liveness analysis). // TODO: identify related phi nodes that can share spill slots, and share them // (also needs liveness). void WinEHPrepare::insertPHIStores(PHINode *OriginalPHI, AllocaInst *SpillSlot) { // Use a worklist of (Block, Value) pairs -- the given Value needs to be // stored to the spill slot by the end of the given Block. SmallVector, 4> Worklist; Worklist.push_back({OriginalPHI->getParent(), OriginalPHI}); while (!Worklist.empty()) { BasicBlock *EHBlock; Value *InVal; std::tie(EHBlock, InVal) = Worklist.pop_back_val(); PHINode *PN = dyn_cast(InVal); if (PN && PN->getParent() == EHBlock) { // The value is defined by another PHI we need to remove, with no room to // insert a store after the PHI, so each predecessor needs to store its // incoming value. for (unsigned i = 0, e = PN->getNumIncomingValues(); i < e; ++i) { Value *PredVal = PN->getIncomingValue(i); // Undef can safely be skipped. if (isa(PredVal)) continue; insertPHIStore(PN->getIncomingBlock(i), PredVal, SpillSlot, Worklist); } } else { // We need to store InVal, which dominates EHBlock, but can't put a store // in EHBlock, so need to put stores in each predecessor. for (BasicBlock *PredBlock : predecessors(EHBlock)) { insertPHIStore(PredBlock, InVal, SpillSlot, Worklist); } } } } void WinEHPrepare::insertPHIStore( BasicBlock *PredBlock, Value *PredVal, AllocaInst *SpillSlot, SmallVectorImpl> &Worklist) { if (PredBlock->isEHPad() && !isa(PredBlock->getFirstNonPHI())) { // Pred is unsplittable, so we need to queue it on the worklist. Worklist.push_back({PredBlock, PredVal}); return; } // Otherwise, insert the store at the end of the basic block. new StoreInst(PredVal, SpillSlot, PredBlock->getTerminator()); } void WinEHPrepare::replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot, DenseMap &Loads, Function &F) { // Lazilly create the spill slot. if (!SpillSlot) SpillSlot = new AllocaInst(V->getType(), nullptr, Twine(V->getName(), ".wineh.spillslot"), &F.getEntryBlock().front()); auto *UsingInst = cast(U.getUser()); if (auto *UsingPHI = dyn_cast(UsingInst)) { // If this is a PHI node, we can't insert a load of the value before // the use. Instead insert the load in the predecessor block // corresponding to the incoming value. // // Note that if there are multiple edges from a basic block to this // PHI node that we cannot have multiple loads. The problem is that // the resulting PHI node will have multiple values (from each load) // coming in from the same block, which is illegal SSA form. // For this reason, we keep track of and reuse loads we insert. BasicBlock *IncomingBlock = UsingPHI->getIncomingBlock(U); if (auto *CatchRet = dyn_cast(IncomingBlock->getTerminator())) { // Putting a load above a catchret and use on the phi would still leave // a cross-funclet def/use. We need to split the edge, change the // catchret to target the new block, and put the load there. BasicBlock *PHIBlock = UsingInst->getParent(); BasicBlock *NewBlock = SplitEdge(IncomingBlock, PHIBlock); // SplitEdge gives us: // IncomingBlock: // ... // br label %NewBlock // NewBlock: // catchret label %PHIBlock // But we need: // IncomingBlock: // ... // catchret label %NewBlock // NewBlock: // br label %PHIBlock // So move the terminators to each others' blocks and swap their // successors. BranchInst *Goto = cast(IncomingBlock->getTerminator()); Goto->removeFromParent(); CatchRet->removeFromParent(); IncomingBlock->getInstList().push_back(CatchRet); NewBlock->getInstList().push_back(Goto); Goto->setSuccessor(0, PHIBlock); CatchRet->setSuccessor(NewBlock); // Update the color mapping for the newly split edge. SetVector &ColorsForPHIBlock = BlockColors[PHIBlock]; BlockColors[NewBlock] = ColorsForPHIBlock; for (BasicBlock *FuncletPad : ColorsForPHIBlock) FuncletBlocks[FuncletPad].insert(NewBlock); // Treat the new block as incoming for load insertion. IncomingBlock = NewBlock; } Value *&Load = Loads[IncomingBlock]; // Insert the load into the predecessor block if (!Load) Load = new LoadInst(SpillSlot, Twine(V->getName(), ".wineh.reload"), /*Volatile=*/false, IncomingBlock->getTerminator()); U.set(Load); } else { // Reload right before the old use. auto *Load = new LoadInst(SpillSlot, Twine(V->getName(), ".wineh.reload"), /*Volatile=*/false, UsingInst); U.set(Load); } } void WinEHFuncInfo::addIPToStateRange(const BasicBlock *PadBB, MCSymbol *InvokeBegin, MCSymbol *InvokeEnd) { assert(PadBB->isEHPad() && EHPadStateMap.count(PadBB->getFirstNonPHI()) && "should get EH pad BB with precomputed state"); InvokeToStateMap[InvokeBegin] = std::make_pair(EHPadStateMap[PadBB->getFirstNonPHI()], InvokeEnd); }