diff options
Diffstat (limited to 'lib/CodeGen/WinEHPrepare.cpp')
-rw-r--r-- | lib/CodeGen/WinEHPrepare.cpp | 345 |
1 files changed, 277 insertions, 68 deletions
diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index 848e8032e7..b368f3b175 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Analysis/LibCallSemantics.h" #include "llvm/CodeGen/WinEHFuncInfo.h" @@ -86,6 +87,8 @@ private: bool prepareExceptionHandlers(Function &F, SmallVectorImpl<LandingPadInst *> &LPads); void promoteLandingPadValues(LandingPadInst *LPad); + void demoteValuesLiveAcrossHandlers(Function &F, + SmallVectorImpl<LandingPadInst *> &LPads); void completeNestedLandingPad(Function *ParentFn, LandingPadInst *OutlinedLPad, const LandingPadInst *OriginalLPad, @@ -326,6 +329,10 @@ static cl::opt<bool> cl::desc("Prepare functions with SEH personalities")); bool WinEHPrepare::runOnFunction(Function &Fn) { + // No need to prepare outlined handlers. + if (Fn.hasFnAttribute("wineh-parent")) + return false; + SmallVector<LandingPadInst *, 4> LPads; SmallVector<ResumeInst *, 4> Resumes; for (BasicBlock &BB : Fn) { @@ -369,8 +376,258 @@ void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired<DominatorTreeWrapperPass>(); } +static bool isSelectorDispatch(BasicBlock *BB, BasicBlock *&CatchHandler, + Constant *&Selector, BasicBlock *&NextBB); + +// Finds blocks reachable from the starting set Worklist. Does not follow unwind +// edges or blocks listed in StopPoints. +static void findReachableBlocks(SmallPtrSetImpl<BasicBlock *> &ReachableBBs, + SetVector<BasicBlock *> &Worklist, + const SetVector<BasicBlock *> *StopPoints) { + while (!Worklist.empty()) { + BasicBlock *BB = Worklist.pop_back_val(); + + // Don't cross blocks that we should stop at. + if (StopPoints && StopPoints->count(BB)) + continue; + + if (!ReachableBBs.insert(BB).second) + continue; // Already visited. + + // Don't follow unwind edges of invokes. + if (auto *II = dyn_cast<InvokeInst>(BB->getTerminator())) { + Worklist.insert(II->getNormalDest()); + continue; + } + + // Otherwise, follow all successors. + Worklist.insert(succ_begin(BB), succ_end(BB)); + } +} + +/// Find all points where exceptional control rejoins normal control flow via +/// llvm.eh.endcatch. Add them to the normal bb reachability worklist. +static void findCXXEHReturnPoints(Function &F, + SetVector<BasicBlock *> &EHReturnBlocks) { + for (auto BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) { + BasicBlock *BB = BBI; + for (Instruction &I : *BB) { + if (match(&I, m_Intrinsic<Intrinsic::eh_endcatch>())) { + // Split the block after the call to llvm.eh.endcatch if there is + // anything other than an unconditional branch, or if the successor + // starts with a phi. + auto *Br = dyn_cast<BranchInst>(I.getNextNode()); + if (!Br || !Br->isUnconditional() || + isa<PHINode>(Br->getSuccessor(0)->begin())) { + DEBUG(dbgs() << "splitting block " << BB->getName() + << " with llvm.eh.endcatch\n"); + BBI = BB->splitBasicBlock(I.getNextNode(), "ehreturn"); + } + // The next BB is normal control flow. + EHReturnBlocks.insert(BB->getTerminator()->getSuccessor(0)); + break; + } + } + } +} + +static bool isCatchAllLandingPad(const BasicBlock *BB) { + const LandingPadInst *LP = BB->getLandingPadInst(); + if (!LP) + return false; + unsigned N = LP->getNumClauses(); + return (N > 0 && LP->isCatch(N - 1) && + isa<ConstantPointerNull>(LP->getClause(N - 1))); +} + +/// Find all points where exceptions control rejoins normal control flow via +/// selector dispatch. +static void findSEHEHReturnPoints(Function &F, + SetVector<BasicBlock *> &EHReturnBlocks) { + for (auto BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) { + BasicBlock *BB = BBI; + // If the landingpad is a catch-all, treat the whole lpad as if it is + // reachable from normal control flow. + // FIXME: This is imprecise. We need a better way of identifying where a + // catch-all starts and cleanups stop. As far as LLVM is concerned, there + // is no difference. + if (isCatchAllLandingPad(BB)) { + EHReturnBlocks.insert(BB); + continue; + } + + BasicBlock *CatchHandler; + BasicBlock *NextBB; + Constant *Selector; + if (isSelectorDispatch(BB, CatchHandler, Selector, NextBB)) { + // Split the edge if there is a phi node. Returning from EH to a phi node + // is just as impossible as having a phi after an indirectbr. + if (isa<PHINode>(CatchHandler->begin())) { + DEBUG(dbgs() << "splitting EH return edge from " << BB->getName() + << " to " << CatchHandler->getName() << '\n'); + BBI = CatchHandler = SplitCriticalEdge( + BB, std::find(succ_begin(BB), succ_end(BB), CatchHandler)); + } + EHReturnBlocks.insert(CatchHandler); + } + } +} + +/// Ensure that all values live into and out of exception handlers are stored +/// in memory. +/// FIXME: This falls down when values are defined in one handler and live into +/// another handler. For example, a cleanup defines a value used only by a +/// catch handler. +void WinEHPrepare::demoteValuesLiveAcrossHandlers( + Function &F, SmallVectorImpl<LandingPadInst *> &LPads) { + DEBUG(dbgs() << "Demoting values live across exception handlers in function " + << F.getName() << '\n'); + + // Build a set of all non-exceptional blocks and exceptional blocks. + // - Non-exceptional blocks are blocks reachable from the entry block while + // not following invoke unwind edges. + // - Exceptional blocks are blocks reachable from landingpads. Analysis does + // not follow llvm.eh.endcatch blocks, which mark a transition from + // exceptional to normal control. + SmallPtrSet<BasicBlock *, 4> NormalBlocks; + SmallPtrSet<BasicBlock *, 4> EHBlocks; + SetVector<BasicBlock *> EHReturnBlocks; + SetVector<BasicBlock *> Worklist; + + if (Personality == EHPersonality::MSVC_CXX) + findCXXEHReturnPoints(F, EHReturnBlocks); + else + findSEHEHReturnPoints(F, EHReturnBlocks); + + DEBUG({ + dbgs() << "identified the following blocks as EH return points:\n"; + for (BasicBlock *BB : EHReturnBlocks) + dbgs() << " " << BB->getName() << '\n'; + }); + + // Join points should not have phis at this point, unless they are a + // landingpad, in which case we will demote their phis later. +#ifndef NDEBUG + for (BasicBlock *BB : EHReturnBlocks) + assert((BB->isLandingPad() || !isa<PHINode>(BB->begin())) && + "non-lpad EH return block has phi"); +#endif + + // Normal blocks are the blocks reachable from the entry block and all EH + // return points. + Worklist = EHReturnBlocks; + Worklist.insert(&F.getEntryBlock()); + findReachableBlocks(NormalBlocks, Worklist, nullptr); + DEBUG({ + dbgs() << "marked the following blocks as normal:\n"; + for (BasicBlock *BB : NormalBlocks) + dbgs() << " " << BB->getName() << '\n'; + }); + + // Exceptional blocks are the blocks reachable from landingpads that don't + // cross EH return points. + Worklist.clear(); + for (auto *LPI : LPads) + Worklist.insert(LPI->getParent()); + findReachableBlocks(EHBlocks, Worklist, &EHReturnBlocks); + DEBUG({ + dbgs() << "marked the following blocks as exceptional:\n"; + for (BasicBlock *BB : EHBlocks) + dbgs() << " " << BB->getName() << '\n'; + }); + + SetVector<Argument *> ArgsToDemote; + SetVector<Instruction *> InstrsToDemote; + for (BasicBlock &BB : F) { + bool IsNormalBB = NormalBlocks.count(&BB); + bool IsEHBB = EHBlocks.count(&BB); + if (!IsNormalBB && !IsEHBB) + continue; // Blocks that are neither normal nor EH are unreachable. + for (Instruction &I : BB) { + for (Value *Op : I.operands()) { + // Don't demote static allocas, constants, and labels. + if (isa<Constant>(Op) || isa<BasicBlock>(Op) || isa<InlineAsm>(Op)) + continue; + auto *AI = dyn_cast<AllocaInst>(Op); + if (AI && AI->isStaticAlloca()) + continue; + + if (auto *Arg = dyn_cast<Argument>(Op)) { + if (IsEHBB) { + DEBUG(dbgs() << "Demoting argument " << *Arg + << " used by EH instr: " << I << "\n"); + ArgsToDemote.insert(Arg); + } + continue; + } + + auto *OpI = cast<Instruction>(Op); + BasicBlock *OpBB = OpI->getParent(); + // If a value is produced and consumed in the same BB, we don't need to + // demote it. + if (OpBB == &BB) + continue; + bool IsOpNormalBB = NormalBlocks.count(OpBB); + bool IsOpEHBB = EHBlocks.count(OpBB); + if (IsNormalBB != IsOpNormalBB || IsEHBB != IsOpEHBB) { + DEBUG({ + dbgs() << "Demoting instruction live in-out from EH:\n"; + dbgs() << "Instr: " << *OpI << '\n'; + dbgs() << "User: " << I << '\n'; + }); + InstrsToDemote.insert(OpI); + } + } + } + } + + // Demote values live into and out of handlers. + // FIXME: This demotion is inefficient. We should insert spills at the point + // of definition, insert one reload in each handler that uses the value, and + // insert reloads in the BB used to rejoin normal control flow. + Instruction *AllocaInsertPt = F.getEntryBlock().getFirstInsertionPt(); + for (Instruction *I : InstrsToDemote) + DemoteRegToStack(*I, false, AllocaInsertPt); + + // Demote arguments separately, and only for uses in EH blocks. + for (Argument *Arg : ArgsToDemote) { + auto *Slot = new AllocaInst(Arg->getType(), nullptr, + Arg->getName() + ".reg2mem", AllocaInsertPt); + SmallVector<User *, 4> Users(Arg->user_begin(), Arg->user_end()); + for (User *U : Users) { + auto *I = dyn_cast<Instruction>(U); + if (I && EHBlocks.count(I->getParent())) { + auto *Reload = new LoadInst(Slot, Arg->getName() + ".reload", false, I); + U->replaceUsesOfWith(Arg, Reload); + } + } + new StoreInst(Arg, Slot, AllocaInsertPt); + } + + // Demote landingpad phis, as the landingpad will be removed from the machine + // CFG. + for (LandingPadInst *LPI : LPads) { + BasicBlock *BB = LPI->getParent(); + while (auto *Phi = dyn_cast<PHINode>(BB->begin())) + DemotePHIToStack(Phi, AllocaInsertPt); + } + + DEBUG(dbgs() << "Demoted " << InstrsToDemote.size() << " instructions and " + << ArgsToDemote.size() << " arguments for WinEHPrepare\n\n"); +} + bool WinEHPrepare::prepareExceptionHandlers( Function &F, SmallVectorImpl<LandingPadInst *> &LPads) { + // Don't run on functions that are already prepared. + for (LandingPadInst *LPad : LPads) { + BasicBlock *LPadBB = LPad->getParent(); + for (Instruction &Inst : *LPadBB) + if (match(&Inst, m_Intrinsic<Intrinsic::eh_actions>())) + return false; + } + + demoteValuesLiveAcrossHandlers(F, LPads); + // These containers are used to re-map frame variables that are used in // outlined catch and cleanup handlers. They will be populated as the // handlers are outlined. @@ -391,11 +648,9 @@ bool WinEHPrepare::prepareExceptionHandlers( bool LPadHasActionList = false; BasicBlock *LPadBB = LPad->getParent(); for (Instruction &Inst : *LPadBB) { - if (auto *IntrinCall = dyn_cast<IntrinsicInst>(&Inst)) { - if (IntrinCall->getIntrinsicID() == Intrinsic::eh_actions) { - LPadHasActionList = true; - break; - } + if (match(&Inst, m_Intrinsic<Intrinsic::eh_actions>())) { + LPadHasActionList = true; + break; } // FIXME: This is here to help with the development of nested landing pad // outlining. It should be removed when that is finished. @@ -493,14 +748,17 @@ bool WinEHPrepare::prepareExceptionHandlers( CallInst::Create(ActionIntrin, ActionArgs, "recover", NewLPadBB); // Add an indirect branch listing possible successors of the catch handlers. - IndirectBrInst *Branch = IndirectBrInst::Create(Recover, 0, NewLPadBB); + SetVector<BasicBlock *> ReturnTargets; for (ActionHandler *Action : Actions) { if (auto *CatchAction = dyn_cast<CatchHandler>(Action)) { - for (auto *Target : CatchAction->getReturnTargets()) { - Branch->addDestination(Target); - } + const auto &CatchTargets = CatchAction->getReturnTargets(); + ReturnTargets.insert(CatchTargets.begin(), CatchTargets.end()); } } + IndirectBrInst *Branch = + IndirectBrInst::Create(Recover, ReturnTargets.size(), NewLPadBB); + for (BasicBlock *Target : ReturnTargets) + Branch->addDestination(Target); } // End for each landingpad // If nothing got outlined, there is no more processing to be done. @@ -543,51 +801,10 @@ bool WinEHPrepare::prepareExceptionHandlers( // Finally, replace all of the temporary allocas for frame variables used in // the outlined handlers with calls to llvm.framerecover. - BasicBlock::iterator II = Entry->getFirstInsertionPt(); - Instruction *AllocaInsertPt = II; for (auto &VarInfoEntry : FrameVarInfo) { Value *ParentVal = VarInfoEntry.first; TinyPtrVector<AllocaInst *> &Allocas = VarInfoEntry.second; - - // If the mapped value isn't already an alloca, we need to spill it if it - // is a computed value or copy it if it is an argument. - AllocaInst *ParentAlloca = dyn_cast<AllocaInst>(ParentVal); - if (!ParentAlloca) { - if (auto *Arg = dyn_cast<Argument>(ParentVal)) { - // Lower this argument to a copy and then demote that to the stack. - // We can't just use the argument location because the handler needs - // it to be in the frame allocation block. - // Use 'select i8 true, %arg, undef' to simulate a 'no-op' instruction. - Value *TrueValue = ConstantInt::getTrue(Context); - Value *UndefValue = UndefValue::get(Arg->getType()); - Instruction *SI = - SelectInst::Create(TrueValue, Arg, UndefValue, - Arg->getName() + ".tmp", AllocaInsertPt); - Arg->replaceAllUsesWith(SI); - // Reset the select operand, because it was clobbered by the RAUW above. - SI->setOperand(1, Arg); - ParentAlloca = DemoteRegToStack(*SI, true, SI); - } else if (auto *PN = dyn_cast<PHINode>(ParentVal)) { - ParentAlloca = DemotePHIToStack(PN, AllocaInsertPt); - } else { - Instruction *ParentInst = cast<Instruction>(ParentVal); - // FIXME: This is a work-around to temporarily handle the case where an - // instruction that is only used in handlers is not sunk. - // Without uses, DemoteRegToStack would just eliminate the value. - // This will fail if ParentInst is an invoke. - if (ParentInst->getNumUses() == 0) { - BasicBlock::iterator InsertPt = ParentInst; - ++InsertPt; - ParentAlloca = - new AllocaInst(ParentInst->getType(), nullptr, - ParentInst->getName() + ".reg2mem", - AllocaInsertPt); - new StoreInst(ParentInst, ParentAlloca, InsertPt); - } else { - ParentAlloca = DemoteRegToStack(*ParentInst, true, AllocaInsertPt); - } - } - } + AllocaInst *ParentAlloca = cast<AllocaInst>(ParentVal); // FIXME: We should try to sink unescaped allocas from the parent frame into // the child frame. If the alloca is escaped, we have to use the lifetime @@ -1357,31 +1574,23 @@ WinEHFrameVariableMaterializer::WinEHFrameVariableMaterializer( } Value *WinEHFrameVariableMaterializer::materializeValueFor(Value *V) { - // If we're asked to materialize a value that is an instruction, we - // temporarily create an alloca in the outlined function and add this - // to the FrameVarInfo map. When all the outlining is complete, we'll - // collect these into a structure, spilling non-alloca values in the - // parent frame as necessary, and replace these temporary allocas with - // GEPs referencing the frame allocation block. - - // If the value is an alloca, the mapping is direct. + // If we're asked to materialize a static alloca, we temporarily create an + // alloca in the outlined function and add this to the FrameVarInfo map. When + // all the outlining is complete, we'll replace these temporary allocas with + // calls to llvm.framerecover. if (auto *AV = dyn_cast<AllocaInst>(V)) { + assert(AV->isStaticAlloca() && + "cannot materialize un-demoted dynamic alloca"); AllocaInst *NewAlloca = dyn_cast<AllocaInst>(AV->clone()); Builder.Insert(NewAlloca, AV->getName()); FrameVarInfo[AV].push_back(NewAlloca); return NewAlloca; } - // For other types of instructions or arguments, we need an alloca based on - // the value's type and a load of the alloca. The alloca will be replaced - // by a GEP, but the load will stay. In the parent function, the value will - // be spilled to a location in the frame allocation block. if (isa<Instruction>(V) || isa<Argument>(V)) { - AllocaInst *NewAlloca = - Builder.CreateAlloca(V->getType(), nullptr, "eh.temp.alloca"); - FrameVarInfo[V].push_back(NewAlloca); - LoadInst *NewLoad = Builder.CreateLoad(NewAlloca, V->getName() + ".reload"); - return NewLoad; + errs() << "Failed to demote instruction used in exception handler:\n"; + errs() << " " << *V << '\n'; + report_fatal_error("WinEHPrepare failed to demote instruction"); } // Don't materialize other values. |