//===-- 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/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); void demoteNonlocalUses(Value *V, std::set &ColorsForBB, Function &F); bool prepareExplicitEH(Function &F, SmallVectorImpl &EntryBlocks); void replaceTerminatePadWithCleanup(Function &F); void colorFunclets(Function &F, SmallVectorImpl &EntryBlocks); void demotePHIsOnFunclets(Function &F); void demoteUsesBetweenFunclets(Function &F); void demoteArgumentUses(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; }; } // 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, std::map> &FuncletChildren) { 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. Worklist.push_back({EntryBlock, EntryBlock}); while (!Worklist.empty()) { BasicBlock *Visiting; BasicBlock *Color; std::tie(Visiting, Color) = Worklist.pop_back_val(); 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).second) 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).second) { Worklist.push_back({NormalSucc, Visiting}); } BasicBlock *UnwindSucc = CatchPad->getUnwindDest(); if (BlockColors[UnwindSucc].insert(Color).second) { 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).second) { Worklist.push_back({Succ, Color}); } } } // 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) { std::set &ColorMapItem = BlockColors[FuncletEntry]; for (BasicBlock *Parent : ColorMapItem) FuncletChildren[Parent].insert(FuncletEntry); ColorMapItem.clear(); ColorMapItem.insert(FuncletEntry); } } void WinEHPrepare::colorFunclets(Function &F, SmallVectorImpl &EntryBlocks) { ::colorFunclets(F, EntryBlocks, BlockColors, FuncletBlocks, FuncletChildren); } void llvm::calculateCatchReturnSuccessorColors(const Function *Fn, WinEHFuncInfo &FuncInfo) { SmallVector EntryBlocks; // colorFunclets needs the set of EntryBlocks, get them using // findFuncletEntryPoints. findFuncletEntryPoints(const_cast(*Fn), EntryBlocks); std::map> BlockColors; std::map> FuncletBlocks; std::map> FuncletChildren; // Figure out which basic blocks belong to which funclets. colorFunclets(const_cast(*Fn), EntryBlocks, BlockColors, FuncletBlocks, FuncletChildren); // 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(); std::set &SuccessorColors = BlockColors[CatchRetSuccessor]; assert(SuccessorColors.size() == 1 && "Expected BB to be monochrome!"); BasicBlock *Color = *SuccessorColors.begin(); if (auto *CPI = dyn_cast(Color->getFirstNonPHI())) Color = CPI->getNormalDest(); // 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::demoteUsesBetweenFunclets(Function &F) { // Turn all inter-funclet uses of a Value into loads and stores. for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) { BasicBlock *BB = &*FI++; std::set &ColorsForBB = BlockColors[BB]; for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) { Instruction *I = &*BI++; // Funclets are permitted to use static allocas. if (auto *AI = dyn_cast(I)) if (AI->isStaticAlloca()) continue; demoteNonlocalUses(I, ColorsForBB, F); } } } void WinEHPrepare::demoteArgumentUses(Function &F) { // Also demote function parameters used in funclets. std::set &ColorsForEntry = BlockColors[&F.getEntryBlock()]; for (Argument &Arg : F.args()) demoteNonlocalUses(&Arg, ColorsForEntry, F); } 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 (BasicBlock *BB : BlocksInFunclet) { std::set &ColorsForBB = BlockColors[BB]; // We don't need to do anything if the block is monochromatic. size_t NumColorsForBB = ColorsForBB.size(); if (NumColorsForBB == 1) continue; // 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); BlocksInFunclet.erase(OldBlock); BlockColors[OldBlock].erase(FuncletPadBB); } // 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(); std::set &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!"); if (!DisableDemotion) { 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); demoteUsesBetweenFunclets(F); demoteArgumentUses(F); } cloneCommonBlocks(F, EntryBlocks); if (!DisableCleanups) { removeImplausibleTerminators(F); cleanupPreparedFunclets(F); } verifyPreparedFunclets(F); BlockColors.clear(); FuncletBlocks.clear(); FuncletChildren.clear(); 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()); } // TODO: Share loads for same-funclet uses (requires dominators if funclets // aren't properly nested). void WinEHPrepare::demoteNonlocalUses(Value *V, std::set &ColorsForBB, Function &F) { // Tokens can only be used non-locally due to control flow involving // unreachable edges. Don't try to demote the token usage, we'll simply // delete the cloned user later. if (isa(V) || isa(V)) return; DenseMap Loads; AllocaInst *SpillSlot = nullptr; for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); UI != UE;) { Use &U = *UI++; auto *UsingInst = cast(U.getUser()); BasicBlock *UsingBB = UsingInst->getParent(); // Is the Use inside a block which is colored the same as the Def? // If so, we don't need to escape the Def because we will clone // ourselves our own private copy. std::set &ColorsForUsingBB = BlockColors[UsingBB]; if (ColorsForUsingBB == ColorsForBB) continue; replaceUseWithLoad(V, U, SpillSlot, Loads, F); } if (SpillSlot) { // Insert stores of the computed value into the stack slot. // We have to be careful if I is an invoke instruction, // because we can't insert the store AFTER the terminator instruction. BasicBlock::iterator InsertPt; if (isa(V)) { InsertPt = F.getEntryBlock().getTerminator()->getIterator(); } else if (isa(V)) { auto *II = cast(V); // We cannot demote invoke instructions to the stack if their normal // edge is critical. Therefore, split the critical edge and create a // basic block into which the store can be inserted. if (!II->getNormalDest()->getSinglePredecessor()) { unsigned SuccNum = GetSuccessorNumber(II->getParent(), II->getNormalDest()); assert(isCriticalEdge(II, SuccNum) && "Expected a critical edge!"); BasicBlock *NewBlock = SplitCriticalEdge(II, SuccNum); assert(NewBlock && "Unable to split critical edge."); // Update the color mapping for the newly split edge. std::set &ColorsForUsingBB = BlockColors[II->getParent()]; BlockColors[NewBlock] = ColorsForUsingBB; for (BasicBlock *FuncletPad : ColorsForUsingBB) FuncletBlocks[FuncletPad].insert(NewBlock); } InsertPt = II->getNormalDest()->getFirstInsertionPt(); } else { InsertPt = cast(V)->getIterator(); ++InsertPt; // Don't insert before PHI nodes or EH pad instrs. for (; isa(InsertPt) || InsertPt->isEHPad(); ++InsertPt) ; } new StoreInst(V, SpillSlot, &*InsertPt); } } 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. std::set &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); }