Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ lib/CodeGen/WinEHPrepare.cpp @@ -196,6 +196,9 @@ virtual CloningAction handleResume(ValueToValueMapTy &VMap, const ResumeInst *Resume, BasicBlock *NewBB) = 0; + virtual CloningAction handleCompare(ValueToValueMapTy &VMap, + const CmpInst *Compare, + BasicBlock *NewBB) = 0; virtual CloningAction handleLandingPad(ValueToValueMapTy &VMap, const LandingPadInst *LPad, BasicBlock *NewBB) = 0; @@ -231,6 +234,8 @@ BasicBlock *NewBB) override; CloningAction handleResume(ValueToValueMapTy &VMap, const ResumeInst *Resume, BasicBlock *NewBB) override; + CloningAction handleCompare(ValueToValueMapTy &VMap, + const CmpInst *Compare, BasicBlock *NewBB) override; CloningAction handleLandingPad(ValueToValueMapTy &VMap, const LandingPadInst *LPad, BasicBlock *NewBB) override; @@ -267,6 +272,8 @@ BasicBlock *NewBB) override; CloningAction handleResume(ValueToValueMapTy &VMap, const ResumeInst *Resume, BasicBlock *NewBB) override; + CloningAction handleCompare(ValueToValueMapTy &VMap, + const CmpInst *Compare, BasicBlock *NewBB) override; CloningAction handleLandingPad(ValueToValueMapTy &VMap, const LandingPadInst *LPad, BasicBlock *NewBB) override; @@ -778,25 +785,32 @@ void WinEHPrepare::addStubInvokeToHandlerIfNeeded(Function *Handler, Value *PersonalityFn) { ReturnInst *Ret = nullptr; + UnreachableInst *Unreached = nullptr; for (BasicBlock &BB : *Handler) { TerminatorInst *Terminator = BB.getTerminator(); // If we find an invoke, there is nothing to be done. auto *II = dyn_cast(Terminator); if (II) return; - // If we've already recorded a return instruction, keep looking for invokes. - if (Ret) - continue; // If we haven't recorded a return instruction yet, try this terminator. - Ret = dyn_cast(Terminator); + if (!Ret) + Ret = dyn_cast(Terminator); + // If we haven't recorded an unreachable instruction, try this terminator. + if (!Unreached) + Unreached = dyn_cast(Terminator); } // If we got this far, the handler contains no invokes. We should have seen // at least one return. We'll insert an invoke of llvm.donothing ahead of // that return. - assert(Ret); - BasicBlock *OldRetBB = Ret->getParent(); - BasicBlock *NewRetBB = SplitBlock(OldRetBB, Ret); + assert(Ret || Unreached); + TerminatorInst *Term; + if (Ret) + Term = Ret; + else + Term = Unreached; + BasicBlock *OldRetBB = Term->getParent(); + BasicBlock *NewRetBB = SplitBlock(OldRetBB, Term); // SplitBlock adds an unconditional branch instruction at the end of the // parent block. We want to replace that with an invoke call, so we can // erase it now. @@ -1044,6 +1058,9 @@ if (auto *Resume = dyn_cast(Inst)) return handleResume(VMap, Resume, NewBB); + if (auto *Cmp = dyn_cast(Inst)) + return handleCompare(VMap, Cmp, NewBB); + if (match(Inst, m_Intrinsic())) return handleBeginCatch(VMap, Inst, NewBB); if (match(Inst, m_Intrinsic())) @@ -1164,6 +1181,30 @@ return CloningDirector::StopCloningBB; } +CloningDirector::CloningAction +WinEHCatchDirector::handleCompare(ValueToValueMapTy &VMap, + const CmpInst *Compare, BasicBlock *NewBB) { + const IntrinsicInst *IntrinCall = nullptr; + if (match(Compare->getOperand(0), m_Intrinsic())) { + IntrinCall = dyn_cast(Compare->getOperand(0)); + } else if (match(Compare->getOperand(1), m_Intrinsic())) { + IntrinCall = dyn_cast(Compare->getOperand(1)); + } + if (IntrinCall) { + Value *Selector = IntrinCall->getArgOperand(0)->stripPointerCasts(); + // This causes a replacement that will collapse the landing pad CFG based + // on the filter function we intend to match. + if (Selector == CurrentSelector->stripPointerCasts()) { + VMap[Compare] = ConstantInt::get(SelectorIDType, 1); + } + else { + VMap[Compare] = ConstantInt::get(SelectorIDType, 0); + } + return CloningDirector::SkipInstruction; + } + return CloningDirector::CloneInstruction; +} + CloningDirector::CloningAction WinEHCleanupDirector::handleLandingPad( ValueToValueMapTy &VMap, const LandingPadInst *LPad, BasicBlock *NewBB) { // The MS runtime will terminate the process if an exception occurs in a @@ -1180,11 +1221,11 @@ CloningDirector::CloningAction WinEHCleanupDirector::handleBeginCatch( ValueToValueMapTy &VMap, const Instruction *Inst, BasicBlock *NewBB) { - // Catch blocks within cleanup handlers will always be unreachable. - // We'll insert an unreachable instruction now, but it will be pruned - // before the cloning process is complete. - BasicBlock::InstListType &InstList = NewBB->getInstList(); - InstList.push_back(new UnreachableInst(NewBB->getContext())); + // Cleanup code may flow into catch blocks or the catch block may be part + // of a branch that will be optimized away. We'll insert a return + // instruction now, but it may be pruned before the cloning process is + // complete. + ReturnInst::Create(NewBB->getContext(), nullptr, NewBB); return CloningDirector::StopCloningBB; } @@ -1250,6 +1291,19 @@ return CloningDirector::StopCloningBB; } +CloningDirector::CloningAction +WinEHCleanupDirector::handleCompare(ValueToValueMapTy &VMap, + const CmpInst *Compare, BasicBlock *NewBB) { + const IntrinsicInst *IntrinCall = nullptr; + if (match(Compare->getOperand(0), m_Intrinsic()) || + match(Compare->getOperand(1), m_Intrinsic())) { + VMap[Compare] = ConstantInt::get(SelectorIDType, 1); + return CloningDirector::SkipInstruction; + } + return CloningDirector::CloneInstruction; + +} + WinEHFrameVariableMaterializer::WinEHFrameVariableMaterializer( Function *OutlinedFn, FrameVarInfoMap &FrameVarInfo) : FrameVarInfo(FrameVarInfo), Builder(OutlinedFn->getContext()) { @@ -1346,10 +1400,21 @@ // See if the clause we're looking for is a catch-all. // If so, the catch begins immediately. - if (isa(LPad->getClause(HandlersFound))) { + Constant *ExpectedSelector = LPad->getClause(HandlersFound)->stripPointerCasts(); + if (isa(ExpectedSelector)) { // The catch all must occur last. assert(HandlersFound == NumClauses - 1); + // There can be additional selector dispatches in the call chain that we + // need to ignore. + BasicBlock *CatchBlock = nullptr; + Constant *Selector; + while (BB && isSelectorDispatch(BB, CatchBlock, Selector, NextBB)) { + DEBUG(dbgs() << " Found extra catch dispatch in block " + << CatchBlock->getName() << "\n"); + BB = NextBB; + } + // For C++ EH, check if there is any interesting cleanup code before we // begin the catch. This is important because cleanups cannot rethrow // exceptions but code called from catches can. For SEH, it isn't @@ -1359,8 +1424,10 @@ findCleanupHandlers(Actions, BB, BB); // Add the catch handler to the action list. - CatchHandler *Action = - new CatchHandler(BB, LPad->getClause(HandlersFound), nullptr); + // Since this is a catch-all handler, the selector won't actually appear + // in the code anywhere. ExpectedSelector here is the constant null ptr + // that we got from the landing pad instruction. + CatchHandler *Action = new CatchHandler(BB, ExpectedSelector, nullptr); CatchHandlerMap[BB] = Action; Actions.insertCatchHandler(Action); DEBUG(dbgs() << " Catch all handler at block " << BB->getName() << "\n"); @@ -1376,12 +1443,21 @@ findCleanupHandlers(Actions, BB, CatchAction->getStartBlock()); assert(CatchAction); - ++HandlersFound; - // Add the catch handler to the action list. - Actions.insertCatchHandler(CatchAction); - DEBUG(dbgs() << " Found catch dispatch in block " - << CatchAction->getStartBlock()->getName() << "\n"); + // When the source program contains multiple nested try blocks the catch + // handlers can get strung together in such a way that we can encounter + // a dispatch for a selector that we've already had a handler for. + if (CatchAction->getSelector()->stripPointerCasts() == ExpectedSelector) { + ++HandlersFound; + + // Add the catch handler to the action list. + DEBUG(dbgs() << " Found catch dispatch in block " + << CatchAction->getStartBlock()->getName() << "\n"); + Actions.insertCatchHandler(CatchAction); + } else { + DEBUG(dbgs() << " Found extra catch dispatch in block " + << CatchAction->getStartBlock()->getName() << "\n"); + } // Move on to the block after the catch handler. BB = NextBB;