Index: include/llvm/Transforms/Utils/Cloning.h =================================================================== --- include/llvm/Transforms/Utils/Cloning.h +++ include/llvm/Transforms/Utils/Cloning.h @@ -144,7 +144,9 @@ ///< Skip this instruction but continue cloning the current basic block. SkipInstruction, ///< Skip this instruction and stop cloning the current basic block. - StopCloningBB + StopCloningBB, + ///< Don't clone the terminator but clone the current block's successors. + CloneSuccessors }; virtual ~CloningDirector() {} Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ lib/CodeGen/WinEHPrepare.cpp @@ -16,8 +16,10 @@ #include "llvm/CodeGen/Passes.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Analysis/LibCallSemantics.h" +#include "llvm/IR/CFG.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" @@ -25,6 +27,8 @@ #include "llvm/IR/Module.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Local.h" #include @@ -36,6 +40,8 @@ namespace { +typedef DenseMap BlockToHandlerMapTy; + struct HandlerAllocas { TinyPtrVector Allocas; int ParentFrameAllocationIndex; @@ -46,7 +52,11 @@ // allocation block, and to remap the frame variable allocas (including // spill locations as needed) to GEPs that get the variable from the // frame allocation structure. -typedef MapVector FrameVarInfoMap; +typedef MapVector FrameVarInfoMap; + +enum ActionType { Catch, Cleanup }; + +class LandingPadActions; class WinEHPrepare : public FunctionPass { std::unique_ptr DwarfPrepare; @@ -69,9 +79,21 @@ private: bool prepareCPPEHHandlers(Function &F, SmallVectorImpl &LPads); - bool outlineCatchHandler(Function *SrcFn, Constant *SelectorType, - LandingPadInst *LPad, CallInst *&EHAlloc, + Function *outlineHandler(ActionType CatchOrCleanup, Function *SrcFn, + Constant *SelectorType, LandingPadInst *LPad, + BasicBlock *StartBB, CallInst *&EHAlloc, AllocaInst *&EHObjPtr, FrameVarInfoMap &VarInfo); + + void mapLandingPadBlocks(LandingPadInst *LPad, LandingPadActions &Actions); + BasicBlock *findSelectorComparison(BasicBlock *BB, + BasicBlock *&CatchHandler, + Constant *&Selector, + BasicBlock *&NextBB, + SmallSet &VisitedBlocks); + BasicBlock *findCleanupCodeStart(BasicBlock *StartBB, BasicBlock *EndBB); + + BlockToHandlerMapTy OutlinedCatchHandlerMap; + BlockToHandlerMapTy OutlinedCleanupHandlerMap; }; class WinEHFrameVariableMaterializer : public ValueMaterializer { @@ -87,12 +109,11 @@ IRBuilder<> Builder; }; -class WinEHCatchDirector : public CloningDirector { +class WinEHCloningDirectorBase : public CloningDirector { public: - WinEHCatchDirector(LandingPadInst *LPI, Function *CatchFn, Value *Selector, - Value *EHObj, FrameVarInfoMap &VarInfo) - : LPI(LPI), CurrentSelector(Selector->stripPointerCasts()), EHObj(EHObj), - Materializer(CatchFn, VarInfo), + WinEHCloningDirectorBase(LandingPadInst *LPI, Function *HandlerFn, + FrameVarInfoMap &VarInfo) + : LPI(LPI), Materializer(HandlerFn, VarInfo), SelectorIDType(Type::getInt32Ty(LPI->getContext())), Int8PtrType(Type::getInt8PtrTy(LPI->getContext())) {} @@ -100,12 +121,29 @@ const Instruction *Inst, BasicBlock *NewBB) override; + void mapLandingPadUsers(LandingPadInst *LPad, ValueToValueMapTy &VMap); + + + virtual CloningAction handleBeginCatch(ValueToValueMapTy &VMap, + const Instruction *Inst, + BasicBlock *NewBB) = 0; + virtual CloningAction handleEndCatch(ValueToValueMapTy &VMap, + const Instruction *Inst, + BasicBlock *NewBB) = 0; + virtual CloningAction handleTypeIdFor(ValueToValueMapTy &VMap, + const Instruction *Inst, + BasicBlock *NewBB) = 0; + virtual CloningAction handleInvoke(ValueToValueMapTy &VMap, + const InvokeInst *Invoke, + BasicBlock *NewBB) = 0; + virtual CloningAction handleResume(ValueToValueMapTy &VMap, + const ResumeInst *Resume, + BasicBlock *NewBB) = 0; + ValueMaterializer *getValueMaterializer() override { return &Materializer; } -private: +protected: LandingPadInst *LPI; - Value *CurrentSelector; - Value *EHObj; WinEHFrameVariableMaterializer Materializer; Type *SelectorIDType; Type *Int8PtrType; @@ -115,7 +153,118 @@ const Value *EHPtrStoreAddr; const Value *SelectorStoreAddr; }; -} // end anonymous namespace + +class WinEHCatchDirector : public WinEHCloningDirectorBase { +public: + WinEHCatchDirector(LandingPadInst *LPI, Function *CatchFn, Value *Selector, + Value *EHObj, FrameVarInfoMap &VarInfo) + : WinEHCloningDirectorBase(LPI, CatchFn, VarInfo), EHObj(EHObj), + CurrentSelector(Selector->stripPointerCasts()) {} + + CloningAction handleBeginCatch(ValueToValueMapTy &VMap, + const Instruction *Inst, + BasicBlock *NewBB) override; + CloningAction handleEndCatch(ValueToValueMapTy &VMap, const Instruction *Inst, + BasicBlock *NewBB) override; + CloningAction handleTypeIdFor(ValueToValueMapTy &VMap, + const Instruction *Inst, + BasicBlock *NewBB) override; + CloningAction handleInvoke(ValueToValueMapTy &VMap, const InvokeInst *Invoke, + BasicBlock *NewBB) override; + CloningAction handleResume(ValueToValueMapTy &VMap, const ResumeInst *Resume, + BasicBlock *NewBB) override; + +private: + Value *CurrentSelector; + Value *EHObj; +}; + +class WinEHCleanupDirector : public WinEHCloningDirectorBase { +public: + WinEHCleanupDirector(LandingPadInst *LPI, Function *CleanupFn, + FrameVarInfoMap &VarInfo) + : WinEHCloningDirectorBase(LPI, CleanupFn, VarInfo) {} + + CloningAction handleBeginCatch(ValueToValueMapTy &VMap, + const Instruction *Inst, + BasicBlock *NewBB) override; + CloningAction handleEndCatch(ValueToValueMapTy &VMap, const Instruction *Inst, + BasicBlock *NewBB) override; + CloningAction handleTypeIdFor(ValueToValueMapTy &VMap, + const Instruction *Inst, + BasicBlock *NewBB) override; + CloningAction handleInvoke(ValueToValueMapTy &VMap, const InvokeInst *Invoke, + BasicBlock *NewBB) override; + CloningAction handleResume(ValueToValueMapTy &VMap, const ResumeInst *Resume, + BasicBlock *NewBB) override; +}; + +class ActionHandler { +public: + ActionHandler(BasicBlock *BB, ActionType Type) : StartBB(BB), Type(Type), OutlinedFn(nullptr) {} + + ActionType getType() const { return Type; } + BasicBlock *getStartBlock() const { return StartBB; } + +private: + BasicBlock *StartBB; + ActionType Type; + Function *OutlinedFn; +}; + +class CatchHandler : public ActionHandler { +public: + CatchHandler(BasicBlock *BB, Constant *Selector) : Selector(Selector), ActionHandler(BB, ActionType::Catch) {} + + // Method for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const ActionHandler *H) { + return H->getType() == ActionType::Catch; + } + + Constant *getSelector() const { return Selector; } + +private: + Constant *Selector; +}; + +class CleanupHandler : public ActionHandler { +public: + CleanupHandler(BasicBlock *BB) : ActionHandler(BB, ActionType::Cleanup) {} + + // Method for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const ActionHandler *H) { + return H->getType() == ActionType::Cleanup; + } +}; + +class LandingPadActions { +public: + LandingPadActions() : HasCleanupHandlers(false) {} + ~LandingPadActions() { + for (ActionHandler *H : Actions) { + delete H; + } + } + + void insertCatchHandler(BasicBlock *StartBB, Constant *Selector) { + Actions.push_back(new CatchHandler(StartBB, Selector)); + } + void insertCleanupHandler(BasicBlock *StartBB) { + Actions.push_back(new CleanupHandler(StartBB)); + HasCleanupHandlers = true; + } + + bool includesCleanup() { return HasCleanupHandlers; } + + SmallVector::iterator begin() { return Actions.begin(); } + SmallVector::iterator end() { return Actions.end(); } + +private: + SmallVector Actions; + bool HasCleanupHandlers; +}; + +}; // end anonymous namespace char WinEHPrepare::ID = 0; INITIALIZE_TM_PASS(WinEHPrepare, "winehprepare", "Prepare Windows exceptions", @@ -188,6 +337,15 @@ bool HandlersOutlined = false; + Module *M = F.getParent(); + LLVMContext &Context = M->getContext(); + + // FIXME: Make this an intrinsic. + // Create a new function to receive the handler contents. + Type *Int8PtrType = Type::getInt8PtrTy(Context); + FunctionType *ActionTy = FunctionType::get(Int8PtrType, true); + Value *ActionIntrin = M->getOrInsertFunction("llvm.eh.actions", ActionTy); + for (LandingPadInst *LPad : LPads) { // Look for evidence that this landingpad has already been processed. bool LPadHasActionList = false; @@ -199,6 +357,12 @@ 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. + if (isa(Inst)) { + LPadHasActionList = true; + break; + } } // If we've already outlined the handlers for this landingpad, @@ -206,16 +370,24 @@ if (LPadHasActionList) continue; - for (unsigned Idx = 0, NumClauses = LPad->getNumClauses(); Idx < NumClauses; - ++Idx) { - if (LPad->isCatch(Idx)) { - // Create a new instance of the handler data structure in the - // HandlerData vector. + LandingPadActions Actions; + mapLandingPadBlocks(LPad, Actions); + + for (ActionHandler *Action : Actions) { + Function *OutlinedFn = nullptr; + BasicBlock *StartBB = Action->getStartBlock(); + if (auto *CatchAction = dyn_cast(Action)) { + // See if this handler has already been outlined. + if (OutlinedCatchHandlerMap.count(StartBB)) + continue; + CallInst *EHAlloc = nullptr; AllocaInst *EHObjPtr = nullptr; - bool Outlined = outlineCatchHandler(&F, LPad->getClause(Idx), LPad, - EHAlloc, EHObjPtr, FrameVarInfo); - if (Outlined) { + OutlinedFn = outlineHandler(ActionType::Catch, &F, + CatchAction->getSelector(), LPad, StartBB, + EHAlloc, EHObjPtr, FrameVarInfo); + if (OutlinedFn) { + OutlinedCatchHandlerMap[StartBB] = OutlinedFn; HandlersOutlined = true; // These values must be resolved after all handlers have been // outlined. @@ -224,20 +396,65 @@ if (EHObjPtr) HandlerEHObjPtrs.push_back(EHObjPtr); } - } // End if (isCatch) - } // End for each clause - } // End for each landingpad + } else { + // See if this handler has already been outlined. + // It is possible to have cleanup and catch code in the same block, so + // these do need to be two separate maps. + if (OutlinedCleanupHandlerMap.count(StartBB)) + continue; + + CallInst *EHAlloc = nullptr; + AllocaInst *IgnoreEHObjPtr = nullptr; + OutlinedFn = outlineHandler(ActionType::Cleanup, &F, nullptr, LPad, StartBB, + EHAlloc, IgnoreEHObjPtr, FrameVarInfo); + if (OutlinedFn) { + OutlinedCleanupHandlerMap[StartBB] = OutlinedFn; + HandlersOutlined = true; + // This value must be resolved after all handlers have been outlined. + if (EHAlloc) + HandlerAllocs.push_back(EHAlloc); + } + } // end else of if-catch-else-cleanup + } // End for each Action + + // FIXME: We need a guard against partially outlined functions. + if (HandlersOutlined) { + // Insert a call to eh_actions + Value *ActionArgs[] = {LPad}; + CallInst *Action = CallInst::Create(ActionIntrin, ActionArgs, "recover", LPadBB); + // FIXME: This needs to get a list of targets from the catch actions. + IndirectBrInst *Branch = IndirectBrInst::Create(Action, 0, LPadBB); + + // Remove all other instructions in this block. + BasicBlock::iterator II = Action; + --II; + while (cast(II) != LPad) { + Instruction *Inst = II; + --II; + Inst->eraseFromParent(); + } + } + } // End for each landingpad // If nothing got outlined, there is no more processing to be done. if (!HandlersOutlined) return false; - // FIXME: We will replace the landingpad bodies with llvm.eh.actions - // calls and indirect branches here and then delete blocks - // which are no longer reachable. That will get rid of the - // handlers that we have outlined. There is code below - // that looks for allocas with no uses in the parent function. - // That will only happen after the pruning is implemented. + // Go through the blocks in the function and delete any that are no longer reachable. + BasicBlock *Entry = &F.getEntryBlock(); + Function::iterator BBI = F.begin(); + while (BBI != F.end()) { + // Check if this block has become dead during inlining or other + // simplifications. Note that the first block will appear dead, as it has + // not yet been wired up properly. + if (cast(BBI) != Entry && (pred_empty(BBI) || + BBI->getSinglePredecessor() == BBI)) { + BasicBlock *DeadBB = BBI++; + DeleteDeadBlock(DeadBB); + continue; + } + ++BBI; + } // Remap the frame variables. SmallVector StructTys; @@ -261,22 +478,32 @@ // all the entries in the HandlerData have been processed this isn't a // problem. for (auto &VarInfoEntry : FrameVarInfo) { - AllocaInst *ParentAlloca = VarInfoEntry.first; + Value *ParentVal = VarInfoEntry.first; HandlerAllocas &AllocaInfo = VarInfoEntry.second; - // If the instruction still has uses in the parent function or if it is - // referenced by more than one handler, add it to the frame allocation - // structure. - if (ParentAlloca->getNumUses() != 0 || AllocaInfo.Allocas.size() > 1) { - Type *VarTy = ParentAlloca->getAllocatedType(); + if (auto *ParentAlloca = dyn_cast(ParentVal)) { + // If the instruction still has uses in the parent function or if it is + // referenced by more than one handler, add it to the frame allocation + // structure. + if (ParentAlloca->getNumUses() != 0 || AllocaInfo.Allocas.size() > 1) { + Type *VarTy = ParentAlloca->getAllocatedType(); + StructTys.push_back(VarTy); + AllocaInfo.ParentFrameAllocationIndex = Idx++; + } else { + // If the variable is not used in the parent frame and it is only used + // in one handler, the alloca can be removed from the parent frame + // and the handler will keep its "temporary" alloca to define the value. + // An element index of -1 is used to indicate this condition. + AllocaInfo.ParentFrameAllocationIndex = -1; + } + } + else { + // FIXME: Sink non-alloca values into the handler if they have no other + // uses in the parent function after outlining and are only used in + // one handler. + Type *VarTy = ParentVal->getType(); StructTys.push_back(VarTy); AllocaInfo.ParentFrameAllocationIndex = Idx++; - } else { - // If the variable is not used in the parent frame and it is only used - // in one handler, the alloca can be removed from the parent frame - // and the handler will keep its "temporary" alloca to define the value. - // An element index of -1 is used to indicate this condition. - AllocaInfo.ParentFrameAllocationIndex = -1; } } @@ -287,9 +514,6 @@ IRBuilder<> Builder(F.getParent()->getContext()); // Create a frame allocation. - Module *M = F.getParent(); - LLVMContext &Context = M->getContext(); - BasicBlock *Entry = &F.getEntryBlock(); Builder.SetInsertPoint(Entry->getFirstInsertionPt()); Function *FrameAllocFn = Intrinsic::getDeclaration(M, Intrinsic::frameallocate); @@ -320,6 +544,7 @@ // that pull the EHObjPtr from the frame alloc structure for (AllocaInst *EHObjPtr : HandlerEHObjPtrs) { Value *EHData = EHDataMap[EHObjPtr->getParent()->getParent()]; + Builder.SetInsertPoint(EHObjPtr); Value *ElementPtr = Builder.CreateConstInBoundsGEP2_32(EHData, 0, 1); EHObjPtr->replaceAllUsesWith(ElementPtr); EHObjPtr->removeFromParent(); @@ -330,11 +555,55 @@ // Finally, replace all of the temporary allocas for frame variables used in // the outlined handlers and the original frame allocas with GEP instructions // that get the equivalent pointer from the frame allocation struct. + Instruction *FrameEHDataInst = dyn_cast(FrameEHData); + BasicBlock::iterator II = FrameEHDataInst; + ++II; + Instruction *AllocaInsertPt = II; for (auto &VarInfoEntry : FrameVarInfo) { - AllocaInst *ParentAlloca = VarInfoEntry.first; + Value *ParentVal = VarInfoEntry.first; HandlerAllocas &AllocaInfo = VarInfoEntry.second; int Idx = AllocaInfo.ParentFrameAllocationIndex; + // 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(ParentVal); + if (!ParentAlloca) { + if (auto *Arg = dyn_cast(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(ParentVal)) + ParentAlloca = DemotePHIToStack(PN, AllocaInsertPt); + else { + Instruction *ParentInst = dyn_cast(ParentVal); + assert(ParentInst); + // 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", InsertPt); + new StoreInst(ParentInst, ParentAlloca, InsertPt); + } + else { + ParentAlloca = DemoteRegToStack(*ParentInst, true, ParentInst); + } + } + } + // If we have an index of -1 for this instruction, it means it isn't used // outside of this handler. In that case, we just keep the "temporary" // alloca in the handler and erase the original alloca from the parent. @@ -352,6 +621,8 @@ ParentAlloca->replaceAllUsesWith(ElementPtr); ParentAlloca->removeFromParent(); ElementPtr->takeName(ParentAlloca); + if (ParentAlloca == AllocaInsertPt) + AllocaInsertPt = dyn_cast(ElementPtr); delete ParentAlloca; // Next replace all outlined allocas that are mapped to it. @@ -372,8 +643,54 @@ return HandlersOutlined; } -bool WinEHPrepare::outlineCatchHandler(Function *SrcFn, Constant *SelectorType, - LandingPadInst *LPad, CallInst *&EHAlloc, +// This function examines a block to determine whether the block ends with a +// conditional branch to a catch handler based on a selector comparison. +// This function is used both by the WinEHPrepare::findSelectorComparison() and +// WinEHCleanupDirector::handleTypeIdFor(). +static bool isSelectorDispatch(BasicBlock *BB, + BasicBlock *&CatchHandler, + Constant *&Selector, + BasicBlock *&NextBB) { + TerminatorInst *Terminator = BB->getTerminator(); + BranchInst *Branch = dyn_cast(Terminator); + if (!Branch || !Branch->isConditional()) + return false; + + CmpInst *Compare = dyn_cast(Branch->getCondition()); + if (!Compare || !Compare->isEquality()) + return false; + + // Check to see if either operand is a call to get an eh selector id. + const IntrinsicInst *Intrin = dyn_cast(Compare->getOperand(0)); + if (!Intrin || Intrin->getIntrinsicID() != llvm::Intrinsic::eh_typeid_for) + Intrin = dyn_cast(Compare->getOperand(1)); + if (!Intrin || Intrin->getIntrinsicID() != llvm::Intrinsic::eh_typeid_for) + return false; + + Selector = dyn_cast(Intrin->getOperand(0)); + if (!Selector) + return false; + + if (Compare->getPredicate() == CmpInst::ICMP_EQ) { + CatchHandler = Branch->getSuccessor(0); + NextBB = Branch->getSuccessor(1); + return true; + } + + if (Compare->getPredicate() == CmpInst::ICMP_NE) { + CatchHandler = Branch->getSuccessor(1); + NextBB = Branch->getSuccessor(0); + return true; + } + + // Unexpected predicate + return false; +} + +Function *WinEHPrepare::outlineHandler(ActionType CatchOrCleanup, + Function *SrcFn, Constant *SelectorType, + LandingPadInst *LPad, + BasicBlock *StartBB, CallInst *&EHAlloc, AllocaInst *&EHObjPtr, FrameVarInfoMap &VarInfo) { Module *M = SrcFn->getParent(); @@ -384,16 +701,24 @@ std::vector ArgTys; ArgTys.push_back(Int8PtrType); ArgTys.push_back(Int8PtrType); - FunctionType *FnType = FunctionType::get(Int8PtrType, ArgTys, false); - Function *CatchHandler = Function::Create( - FnType, GlobalVariable::ExternalLinkage, SrcFn->getName() + ".catch", M); + Function *Handler; + if (CatchOrCleanup == Catch) { + FunctionType *FnType = FunctionType::get(Int8PtrType, ArgTys, false); + Handler = Function::Create(FnType, GlobalVariable::ExternalLinkage, + SrcFn->getName() + ".catch", M); + } else { + FunctionType *FnType = + FunctionType::get(Type::getVoidTy(Context), ArgTys, false); + Handler = Function::Create(FnType, GlobalVariable::ExternalLinkage, + SrcFn->getName() + ".cleanup", M); + } // Generate a standard prolog to setup the frame recovery structure. IRBuilder<> Builder(Context); - BasicBlock *Entry = BasicBlock::Create(Context, "catch.entry"); - CatchHandler->getBasicBlockList().push_front(Entry); + BasicBlock *Entry = BasicBlock::Create(Context, "entry"); + Handler->getBasicBlockList().push_front(Entry); Builder.SetInsertPoint(Entry); - Builder.SetCurrentDebugLocation(LPad->getDebugLoc()); + Builder.SetCurrentDebugLocation(StartBB->getFirstNonPHI()->getDebugLoc()); // The outlined handler will be called with the parent's frame pointer as // its second argument. To enable the handler to access variables from @@ -409,46 +734,105 @@ Function *RecoverFrameFn = Intrinsic::getDeclaration(M, Intrinsic::framerecover); Value *RecoverArgs[] = {Builder.CreateBitCast(SrcFn, Int8PtrType, ""), - &(CatchHandler->getArgumentList().back())}; + &(Handler->getArgumentList().back())}; EHAlloc = Builder.CreateCall(RecoverFrameFn, RecoverArgs, "eh.alloc"); - // This alloca is only temporary. We'll be replacing it once we know all the - // frame variables that need to go in the frame allocation structure. - EHObjPtr = Builder.CreateAlloca(Int8PtrType, 0, "eh.obj.ptr"); - - // This will give us a raw pointer to the exception object, which - // corresponds to the formal parameter of the catch statement. If the - // handler uses this object, we will generate code during the outlining - // process to cast the pointer to the appropriate type and deference it - // as necessary. The un-outlined landing pad code represents the - // exception object as the result of the llvm.eh.begincatch call. - Value *EHObj = Builder.CreateLoad(EHObjPtr, false, "eh.obj"); + std::unique_ptr Director; - ValueToValueMapTy VMap; + if (CatchOrCleanup == Catch) { + // This alloca is only temporary. We'll be replacing it once we know all + // the + // frame variables that need to go in the frame allocation structure. + EHObjPtr = Builder.CreateAlloca(Int8PtrType, 0, "eh.obj.ptr"); + + // This will give us a raw pointer to the exception object, which + // corresponds to the formal parameter of the catch statement. If the + // handler uses this object, we will generate code during the outlining + // process to cast the pointer to the appropriate type and deference it + // as necessary. The un-outlined landing pad code represents the + // exception object as the result of the llvm.eh.begincatch call. + Value *EHObj = Builder.CreateLoad(EHObjPtr, false, "eh.obj"); + + Director.reset( + new WinEHCatchDirector(LPad, Handler, SelectorType, EHObj, VarInfo)); + } else { + Director.reset(new WinEHCleanupDirector(LPad, Handler, VarInfo)); + } - // FIXME: Map other values referenced in the filter handler. + ValueToValueMapTy VMap; - WinEHCatchDirector Director(LPad, CatchHandler, SelectorType, EHObj, VarInfo); + Director->mapLandingPadUsers(LPad, VMap); SmallVector Returns; ClonedCodeInfo InlinedFunctionInfo; - BasicBlock::iterator II = LPad; + BasicBlock::iterator II = StartBB->begin(); + if (cast(II) == LPad) + ++II; - CloneAndPruneIntoFromInst(CatchHandler, SrcFn, ++II, VMap, - /*ModuleLevelChanges=*/false, Returns, "", - &InlinedFunctionInfo, - SrcFn->getParent()->getDataLayout(), &Director); + CloneAndPruneIntoFromInst( + Handler, SrcFn, II, VMap, + /*ModuleLevelChanges=*/false, Returns, "", &InlinedFunctionInfo, + SrcFn->getParent()->getDataLayout(), Director.get()); // Move all the instructions in the first cloned block into our entry block. BasicBlock *FirstClonedBB = std::next(Function::iterator(Entry)); Entry->getInstList().splice(Entry->end(), FirstClonedBB->getInstList()); FirstClonedBB->eraseFromParent(); - return true; + return Handler; +} + +void WinEHCloningDirectorBase::mapLandingPadUsers(LandingPadInst *LPad, + ValueToValueMapTy &VMap) { + // The landingpad instruction returns an aggregate value. Typically, its + // value will be passed to a pair of extract value instructions and the + // results of those extracts are often passed to store instructions. + // None of these values is cloned when we outline handlers, but we do + // need to track uses of these values so that we can handle them cleanly. + for (User *U : LPad->users()) { + ExtractValueInst *Extract = dyn_cast(U); + if (Extract) { + assert(Extract->getNumIndices() == 1 && + "Unexpected operation: extracting both landing pad values"); + assert((*(Extract->idx_begin()) == 0 || *(Extract->idx_begin()) == 1) && + "Unexpected operation: extracting an unknown landing pad element"); + + if (*(Extract->idx_begin()) == 0) { + // Element 0 doesn't directly corresponds to anything in the WinEH scheme. + // It will be stored to a memory location, then later loaded and finally + // the loaded value will be used as the argument to an llvm.eh.begincatch + // call. We're tracking it here so that we can skip the store and load. + ExtractedEHPtr = Extract; + } else if (*(Extract->idx_begin()) == 1) { + // Element 1 corresponds to the filter selector. We'll map it to 1 for + // matching purposes, but it will also probably be stored to memory and + // reloaded, so we need to track the instuction so that we can map the + // loaded value too. + VMap[Extract] = ConstantInt::get(SelectorIDType, 1); + ExtractedSelector = Extract; + } else { + continue; + } + + // Look for stores of the extracted values. + for (User *EU : Extract->users()) { + const StoreInst *Store = dyn_cast(EU); + // FIXME: Handle multiple stores of each value? + if (Store) { + if (Extract == ExtractedEHPtr) { + EHPtrStoreAddr = Store->getPointerOperand(); + } + else if (Extract == ExtractedSelector) { + SelectorStoreAddr = Store->getPointerOperand(); + } + } + } + } + } } -CloningDirector::CloningAction WinEHCatchDirector::handleInstruction( +CloningDirector::CloningAction WinEHCloningDirectorBase::handleInstruction( ValueToValueMapTy &VMap, const Instruction *Inst, BasicBlock *NewBB) { // Intercept instructions which extract values from the landing pad aggregate. if (auto *Extract = dyn_cast(Inst)) { @@ -518,67 +902,343 @@ return CloningDirector::CloneInstruction; } - if (match(Inst, m_Intrinsic())) { - // The argument to the call is some form of the first element of the - // landingpad aggregate value, but that doesn't matter. It isn't used - // here. - // The return value of this instruction, however, is used to access the - // EH object pointer. We have generated an instruction to get that value - // from the EH alloc block, so we can just map to that here. - VMap[Inst] = EHObj; + // Nested landing pads will be cloned as stubs, with just the + // landingpad instruction and an unreachable instruction. When + // all landingpads have been outlined, we'll replace this with the + // llvm.eh.actions call and indirect branch created when the + // landing pad was outlined. + const LandingPadInst *NestedLPad = dyn_cast(Inst); + if (NestedLPad) { + Instruction *NewInst = NestedLPad->clone(); + if (NestedLPad->hasName()) + NewInst->setName(NestedLPad->getName()); + // FIXME: Store this mapping somewhere else also. + VMap[NestedLPad] = NewInst; + BasicBlock::InstListType &InstList = NewBB->getInstList(); + InstList.push_back(NewInst); + InstList.push_back(new UnreachableInst(NewBB->getContext())); + return CloningDirector::StopCloningBB; + } + + if (auto *Invoke = dyn_cast(Inst)) + return handleInvoke(VMap, Invoke, NewBB); + + if (auto *Resume = dyn_cast(Inst)) + return handleResume(VMap, Resume, NewBB); + + if (match(Inst, m_Intrinsic())) + return handleBeginCatch(VMap, Inst, NewBB); + if (match(Inst, m_Intrinsic())) + return handleEndCatch(VMap, Inst, NewBB); + if (match(Inst, m_Intrinsic())) + return handleTypeIdFor(VMap, Inst, NewBB); + + // Continue with the default cloning behavior. + return CloningDirector::CloneInstruction; +} + +CloningDirector::CloningAction WinEHCatchDirector::handleBeginCatch( + ValueToValueMapTy &VMap, const Instruction *Inst, BasicBlock *NewBB) { + // The argument to the call is some form of the first element of the + // landingpad aggregate value, but that doesn't matter. It isn't used + // here. + // The return value of this instruction, however, is used to access the + // EH object pointer. We have generated an instruction to get that value + // from the EH alloc block, so we can just map to that here. + VMap[Inst] = EHObj; + return CloningDirector::SkipInstruction; +} + +CloningDirector::CloningAction +WinEHCatchDirector::handleEndCatch(ValueToValueMapTy &VMap, + const Instruction *Inst, BasicBlock *NewBB) { + auto *IntrinCall = dyn_cast(Inst); + // It might be interesting to track whether or not we are inside a catch + // function, but that might make the algorithm more brittle than it needs + // to be. + + // The end catch call can occur in one of two places: either in a + // landingpad + // block that is part of the catch handlers exception mechanism, or at the + // end of the catch block. If it occurs in a landing pad, we must skip it + // and continue so that the landing pad gets cloned. + // FIXME: This case isn't fully supported yet and shouldn't turn up in any + // of the test cases until it is. + if (IntrinCall->getParent()->isLandingPad()) return CloningDirector::SkipInstruction; + + // If an end catch occurs anywhere else the next instruction should be an + // unconditional branch instruction that we want to replace with a return + // to the the address of the branch target. + const BasicBlock *EndCatchBB = IntrinCall->getParent(); + const TerminatorInst *Terminator = EndCatchBB->getTerminator(); + const BranchInst *Branch = dyn_cast(Terminator); + assert(Branch && Branch->isUnconditional()); + assert(std::next(BasicBlock::const_iterator(IntrinCall)) == + BasicBlock::const_iterator(Branch)); + + ReturnInst::Create(NewBB->getContext(), + BlockAddress::get(Branch->getSuccessor(0)), NewBB); + + // We just added a terminator to the cloned block. + // Tell the caller to stop processing the current basic block so that + // the branch instruction will be skipped. + return CloningDirector::StopCloningBB; +} + +CloningDirector::CloningAction WinEHCatchDirector::handleTypeIdFor( + ValueToValueMapTy &VMap, const Instruction *Inst, BasicBlock *NewBB) { + auto *IntrinCall = dyn_cast(Inst); + 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) + VMap[Inst] = ConstantInt::get(SelectorIDType, 1); + else + VMap[Inst] = ConstantInt::get(SelectorIDType, 0); + // Tell the caller not to clone this instruction. + return CloningDirector::SkipInstruction; +} + +// This function parses a landing pad to see if all it does is resume. +// If so, it would be called from within another landing pad for +// exception handling, but since it does nothing any invoke that leads +// to this landing pad can be replaced with a call and an unconditional +// branch to the normal destination. Any exceptions that occur within the +// called function will be handled by the normal runtime exception handling +// process. +static bool isLandingPadEmptyResume(const BasicBlock *LPadBB) { + assert(LPadBB->isLandingPad()); + const LandingPadInst *LPad = LPadBB->getLandingPadInst(); + const BasicBlock *BB = LPadBB; + + const Value *ExtractedEHPtr = nullptr; + const Value *ExtractedSelector = nullptr; + const Value *EHPtrStoreAddr = nullptr; + const Value *SelectorStoreAddr = nullptr; + const Value *LoadedEHPtr = nullptr; + const Value *LoadedSelector = nullptr; + + + // FIXME: This code is mostly shared with the outlining directors. Re-use it. + while (BB) { + for (const Instruction &Inst : BB->getInstList()) { + // Skip the landing pad instruction. We'd have to pass an invoke to get + // to any landing pad other than where we started, so there's no need + // to verify that this is our starting point. + const LandingPadInst *FoundLPad = dyn_cast(&Inst); + if (FoundLPad) + continue; + + const ExtractValueInst *Extract = dyn_cast(&Inst); + if (Extract && Extract->getAggregateOperand() == LPad) { + + assert(Extract->getNumIndices() == 1 && + "Unexpected operation: extracting both landing pad values"); + assert((*(Extract->idx_begin()) == 0 || *(Extract->idx_begin()) == 1) && + "Unexpected operation: extracting an unknown landing pad element"); + + if (*(Extract->idx_begin()) == 0) { + ExtractedEHPtr = Extract; + continue; + } else { + ExtractedSelector = Extract; + continue; + } + + // Any other extract constitutes non-trival content. + return false; + } + + const StoreInst *Store = dyn_cast(&Inst); + if (Store) { + // Look for and suppress stores of the extracted landingpad values. + const Value *StoredValue = Store->getValueOperand(); + if (StoredValue == ExtractedEHPtr) { + EHPtrStoreAddr = Store->getPointerOperand(); + continue; + } + if (StoredValue == ExtractedSelector) { + SelectorStoreAddr = Store->getPointerOperand(); + continue; + } + + // Any other store constitutes non-trival content. + return false; + } + + // There should be a call to llvm.eh.endcatch somewhere in the landing pad. + const IntrinsicInst *IntrinCall = dyn_cast(&Inst); + if (IntrinCall && IntrinCall->getIntrinsicID() == Intrinsic::eh_endcatch) + continue; + + const LoadInst *Load = dyn_cast(&Inst); + if (Load) { + // Look for loads of (previously suppressed) landingpad values. + // The EHPtr load can be ignored (it should only be used as + // an argument to llvm.eh.begincatch), but the selector value + // needs to be mapped to a constant value of 1 to be used to + // simplify the branching to always flow to the current handler. + const Value *LoadAddr = Load->getPointerOperand(); + if (LoadAddr == EHPtrStoreAddr) { + LoadedEHPtr = Load; + continue; + } + if (LoadAddr == SelectorStoreAddr) { + LoadedSelector = Load; + continue; + } + + // Any other load constitutes non-trival content. + return false; + } + + const InsertValueInst *Insert = dyn_cast(&Inst); + if (Insert) { + if (Insert->getInsertedValueOperand() == LoadedEHPtr || + Insert->getInsertedValueOperand() == LoadedSelector) + continue; + // Any other insert constitutes non-trival content. + return false; + } + + // If we reach a resume instruction without having bailed out for some + // other reason, the landing pad is an empty resume. + if (isa(Inst)) + return true; + + // It's likely that the landingpad block will unconditionally branch to + // another block which actually executes the resume. This code handles + // that scenario. + const BranchInst *Branch = dyn_cast(&Inst); + if (Branch) { + if (!Branch->isUnconditional()) + return false; + BB = Branch->getSuccessor(0); + continue; + } + + // Any other instruction is outside the pattern we were looking for. + return false; + } // End for (Instruction) + } // End while (BB) + + return false; +} + +CloningDirector::CloningAction WinEHCatchDirector::handleInvoke( + ValueToValueMapTy &VMap, const InvokeInst *Invoke, BasicBlock *NewBB) { + if (isLandingPadEmptyResume(Invoke->getUnwindDest())) { + SmallVector CallArgs(Invoke->op_begin(), Invoke->op_end() - 3); + // Insert a normal call instruction... + CallInst *NewCall = CallInst::Create(const_cast( + Invoke->getCalledValue()), + CallArgs, Invoke->getName(), NewBB); + NewCall->setCallingConv(Invoke->getCallingConv()); + NewCall->setAttributes(Invoke->getAttributes()); + NewCall->setDebugLoc(Invoke->getDebugLoc()); + + RemapInstruction(NewCall, VMap, RF_None, getTypeRemapper(), + getValueMaterializer()); + + VMap[Invoke] = NewCall; + + // Insert an unconditional branch to the normal destination. + BranchInst::Create(Invoke->getNormalDest(), NewBB); + + // The unwind destination won't be cloned into the new function, so + // we don't need to clean up its phi nodes. + + // We've added a new terminator so we want to stop cloning this block, but + // we need the successor of the block to be added to the work list. + return CloningDirector::CloneSuccessors; } - if (match(Inst, m_Intrinsic())) { - auto *IntrinCall = dyn_cast(Inst); - // It might be interesting to track whether or not we are inside a catch - // function, but that might make the algorithm more brittle than it needs - // to be. - - // The end catch call can occur in one of two places: either in a - // landingpad - // block that is part of the catch handlers exception mechanism, or at the - // end of the catch block. If it occurs in a landing pad, we must skip it - // and continue so that the landing pad gets cloned. - // FIXME: This case isn't fully supported yet and shouldn't turn up in any - // of the test cases until it is. - if (IntrinCall->getParent()->isLandingPad()) - return CloningDirector::SkipInstruction; + return CloningDirector::CloneInstruction; +} + +CloningDirector::CloningAction +WinEHCatchDirector::handleResume(ValueToValueMapTy &VMap, + const ResumeInst *Resume, BasicBlock *NewBB) { + // Resume instructions shouldn't be reachable from catch handlers. + // We still need to handle it, but it will be pruned. + BasicBlock::InstListType &InstList = NewBB->getInstList(); + InstList.push_back(new UnreachableInst(NewBB->getContext())); + return CloningDirector::StopCloningBB; +} + +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())); + return CloningDirector::StopCloningBB; +} - // If an end catch occurs anywhere else the next instruction should be an - // unconditional branch instruction that we want to replace with a return - // to the the address of the branch target. - const BasicBlock *EndCatchBB = IntrinCall->getParent(); - const TerminatorInst *Terminator = EndCatchBB->getTerminator(); - const BranchInst *Branch = dyn_cast(Terminator); - assert(Branch && Branch->isUnconditional()); - assert(std::next(BasicBlock::const_iterator(IntrinCall)) == - BasicBlock::const_iterator(Branch)); - - ReturnInst::Create(NewBB->getContext(), - BlockAddress::get(Branch->getSuccessor(0)), NewBB); - - // We just added a terminator to the cloned block. - // Tell the caller to stop processing the current basic block so that - // the branch instruction will be skipped. +CloningDirector::CloningAction WinEHCleanupDirector::handleEndCatch( + 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())); + return CloningDirector::StopCloningBB; +} + +CloningDirector::CloningAction WinEHCleanupDirector::handleTypeIdFor( + ValueToValueMapTy &VMap, const Instruction *Inst, BasicBlock *NewBB) { + // If we encounter a selector comparison while cloning a cleanup handler, + // we want to stop cloning immediately. Anything after the dispatch + // will be outlined into a different handler. + BasicBlock *CatchHandler; + Constant *Selector; + BasicBlock *NextBB; + if (isSelectorDispatch(const_cast(Inst->getParent()), + CatchHandler, Selector, NextBB)) { + ReturnInst::Create(NewBB->getContext(), nullptr, NewBB); return CloningDirector::StopCloningBB; } - if (match(Inst, m_Intrinsic())) { - auto *IntrinCall = dyn_cast(Inst); - 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) - VMap[Inst] = ConstantInt::get(SelectorIDType, 1); - else - VMap[Inst] = ConstantInt::get(SelectorIDType, 0); - // Tell the caller not to clone this instruction. - return CloningDirector::SkipInstruction; - } + // If eg.typeid.for is called for any other reason, it can be ignored. + VMap[Inst] = ConstantInt::get(SelectorIDType, 0); + return CloningDirector::SkipInstruction; +} - // Continue with the default cloning behavior. - return CloningDirector::CloneInstruction; +CloningDirector::CloningAction WinEHCleanupDirector::handleResume( + ValueToValueMapTy &VMap, const ResumeInst *Resume, BasicBlock *NewBB) { + ReturnInst::Create(NewBB->getContext(), nullptr, NewBB); + + // We just added a terminator to the cloned block. + // Tell the caller to stop processing the current basic block so that + // the branch instruction will be skipped. + return CloningDirector::StopCloningBB; +} + +CloningDirector::CloningAction WinEHCleanupDirector::handleInvoke( + ValueToValueMapTy &VMap, const InvokeInst *Invoke, BasicBlock *NewBB) { + // All invokes in cleanup handlers can be replaced with calls. + SmallVector CallArgs(Invoke->op_begin(), Invoke->op_end() - 3); + // Insert a normal call instruction... + CallInst *NewCall = CallInst::Create(const_cast( + Invoke->getCalledValue()), + CallArgs, Invoke->getName(), NewBB); + NewCall->setCallingConv(Invoke->getCallingConv()); + NewCall->setAttributes(Invoke->getAttributes()); + NewCall->setDebugLoc(Invoke->getDebugLoc()); + VMap[Invoke] = NewCall; + + // Insert an unconditional branch to the normal destination. + BranchInst::Create(Invoke->getNormalDest(), NewBB); + + // The unwind destination won't be cloned into the new function, so + // we don't need to clean up its phi nodes. + + // We just added a terminator to the cloned block. + // Tell the caller to stop processing the current basic block. + return CloningDirector::StopCloningBB; } + WinEHFrameVariableMaterializer::WinEHFrameVariableMaterializer( Function *OutlinedFn, FrameVarInfoMap &FrameVarInfo) : FrameVarInfo(FrameVarInfo), Builder(OutlinedFn->getContext()) { @@ -588,38 +1248,342 @@ } Value *WinEHFrameVariableMaterializer::materializeValueFor(Value *V) { - // If we're asked to materialize an alloca variable, we temporarily - // create a matching alloca in the outlined function. When all the - // outlining is complete, we'll collect these into a structure and - // replace these temporary allocas with GEPs referencing the frame - // allocation block. + // 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 (auto *AV = dyn_cast(V)) { - AllocaInst *NewAlloca = Builder.CreateAlloca( - AV->getAllocatedType(), AV->getArraySize(), AV->getName()); + AllocaInst *NewAlloca = dyn_cast(AV->clone()); + Builder.Insert(NewAlloca, AV->getName()); FrameVarInfo[AV].Allocas.push_back(NewAlloca); return NewAlloca; } -// FIXME: Do PHI nodes need special handling? + // 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(V) || isa(V)) { + AllocaInst *NewAlloca = Builder.CreateAlloca( + V->getType(), nullptr, "eh.temp.alloca"); + FrameVarInfo[V].Allocas.push_back(NewAlloca); + LoadInst *NewLoad = Builder.CreateLoad( + NewAlloca, V->getName() + ".reload"); + return NewLoad; + } -// FIXME: Are there other cases we can handle better? GEP, ExtractValue, etc. + // Don't materialize other values. + return nullptr; +} -// FIXME: This doesn't work during cloning because it finds an instruction -// in the use list that isn't yet part of a basic block. -#if 0 - // If we're asked to remap some other instruction, we'll need to - // spill it to an alloca variable in the parent function and add a - // temporary alloca in the outlined function to be processed as - // described above. - Instruction *Inst = dyn_cast(V); - if (Inst) { - AllocaInst *Spill = DemoteRegToStack(*Inst, true); - AllocaInst *NewAlloca = Builder.CreateAlloca(Spill->getAllocatedType(), - Spill->getArraySize()); - FrameVarMap[AV] = NewAlloca; - return NewAlloca; +// This function maps the catch and cleanup handlers that are reachable from the +// specified landing pad. The landing pad sequence will have this basic shape: +// +// +// +// +// +// +// +// +// ... +// +// Any of the cleanup slots may be absent. The cleanup slots may be occupied by +// any arbitrary control flow, but all paths through the cleanup code must +// eventually reach the next selector comparison and no path can skip to a +// different selector comparisons, though some paths may terminate abnormally. +// Therefore, we will use a depth first search from the start of any given +// cleanup block and stop searching when we find the next selector comparison. +// +// If the landingpad instruction does not have a catch clause, we will assume +// that any instructions other than selector comparisons and catch handlers can +// be ignored. In practice, these will only be the boilerplate instructions. +// +// The catch handlers may also have any control structure, but we are only +// interested in the start of the catch handlers, so we don't need to actually +// follow the flow of the catch handlers. The start of the catch handlers can +// be located from the compare instructions, but they can be skipped in the +// flow by following the contrary branch. +// +void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad, LandingPadActions &Actions) { + unsigned int NumClauses = LPad->getNumClauses(); + unsigned int HandlersFound = 0; + BasicBlock *BB = LPad->getParent(); + + // FIXME: Wrap this with DEBUG + DEBUG(dbgs() << "Mapping landing pad: " << BB->getName() << "\n"); + + if (NumClauses == 0) { + // This landing pad contains only cleanup code. + Actions.insertCleanupHandler(BB); + DEBUG(dbgs() << " Assuming cleanup code in block " << BB->getName() << "\n"); + assert(LPad->isCleanup()); + return; + } + + SmallSet VisitedBlocks; + + while (HandlersFound != NumClauses) { + BasicBlock *CatchHandler = nullptr; + Constant *Selector = nullptr; + BasicBlock *NextBB = nullptr; + + // See if the clause we're looking for is a catch-all. + if (isa(LPad->getClause(HandlersFound))) { + // The catch all must occur last. + assert(HandlersFound == NumClauses - 1); + + // See if there is any interesting code executed before the catch. + BasicBlock *CleanupBB = findCleanupCodeStart(BB, BB); + if (CleanupBB) { + // Add a cleanup entry to the list + Actions.insertCleanupHandler(CleanupBB); + DEBUG(dbgs() << " Found cleanup code in block " << CleanupBB->getName() << "\n"); + } + + // Add the catch handler to the action list. + Actions.insertCatchHandler(BB, LPad->getClause(HandlersFound)); + DEBUG(dbgs() << " Catch handler at block " << CatchHandler->getName() << "\n"); + ++HandlersFound; + continue; + } + + BasicBlock *DispatchBB = findSelectorComparison(BB, + CatchHandler, + Selector, + NextBB, + VisitedBlocks); + // See if there is any interesting code executed before the dispatch. + BasicBlock *CleanupBB = findCleanupCodeStart(BB, DispatchBB); + if (CleanupBB) { + // Add a cleanup entry to the list + Actions.insertCleanupHandler(CleanupBB); + DEBUG(dbgs() << " Found cleanup code in block " << CleanupBB->getName() << "\n"); + } + + assert(DispatchBB); + ++HandlersFound; + + BranchInst *Dispatch = dyn_cast(DispatchBB->getTerminator()); + assert(Dispatch); + + // Add the catch handler to the action list. + Actions.insertCatchHandler(DispatchBB, Selector); + DEBUG(dbgs() << " Found catch handler in block " << CatchHandler->getName() << "\n"); + + // Move on to the block after the catch handler. + BB = NextBB; + } + + // See if there is any interesting code executed before the resume. + BasicBlock *CleanupBB = findCleanupCodeStart(BB, BB); + if (CleanupBB) { + // Add a cleanup entry to the list + Actions.insertCleanupHandler(CleanupBB); + DEBUG(dbgs() << " Found cleanup code in block " << CleanupBB->getName() << "\n"); + } + + // It's possible that some optimization moved code into a landingpad that wasn't + // previously being used for cleanup. If that happens, we need to execute that + // extra code from a cleanup handler. + if (Actions.includesCleanup() && !LPad->isCleanup()) + LPad->setCleanup(true); +} + +// This function searches starting with the input block for the next +// block that terminates with a branch whose condition is based on a selector +// comparison. This may be the input block. See the mapLandingPadBlocks +// comments for a discussion of control flow assumptions. +// +BasicBlock *WinEHPrepare::findSelectorComparison(BasicBlock *BB, + BasicBlock *&CatchHandler, + Constant *&Selector, + BasicBlock *&NextBB, + SmallSet &VisitedBlocks) { + VisitedBlocks.insert(BB); + + if (isSelectorDispatch(BB, CatchHandler, Selector, NextBB)) + return BB; + + for (BasicBlock *Succ : successors(BB)) { + if (VisitedBlocks.count(Succ)) + continue; + + BasicBlock *DispatchBB = findSelectorComparison(Succ, CatchHandler, Selector, NextBB, VisitedBlocks); + if (DispatchBB) + return DispatchBB; } -#endif + return nullptr; +} +// This function searches starting with the input block for the next block that +// contains code that is not part of a catch handler and would not be eliminated +// during handler outlining. +// +BasicBlock *WinEHPrepare::findCleanupCodeStart(BasicBlock *StartBB, BasicBlock *EndBB) { + // Here we will skip over the following: + // + // landing pad prolog: + // + // Unconditional branches + // + // Selector dispatch + // + // Resume pattern + // + // Anything else marks the start of an interesting block + + BasicBlock *BB = StartBB; + // Anything other than an unconditional branch will kick us out of this loop + // one way or another. + while (BB) { + TerminatorInst *Terminator = BB->getTerminator(); + + // Look for the bare resume pattern: + // %exn2 = load i8** %exn.slot + // %sel2 = load i32* %ehselector.slot + // %lpad.val1 = insertvalue { i8*, i32 } undef, i8* %exn2, 0 + // %lpad.val2 = insertvalue { i8*, i32 } %lpad.val1, i32 %sel2, 1 + // resume { i8*, i32 } %lpad.val2 + ResumeInst *Resume = dyn_cast(Terminator); + if (Resume) { + InsertValueInst *Insert1 = nullptr; + InsertValueInst *Insert2 = nullptr; + if (!isa(Resume->getOperand(0))) { + Insert2 = dyn_cast(Resume->getOperand(0)); + if (!Insert2) + return BB; + Insert1 = dyn_cast(Insert2->getAggregateOperand()); + if (!Insert1) + return BB; + } + for (BasicBlock::iterator II = BB->begin(), IE = BB->end(); II != IE; ++II) { + Instruction *Inst = II; + if (isa(Inst) || isa(Inst)) + continue; + if (Inst == Insert1 || Inst == Insert2 || Inst == Resume) + continue; + if (!Inst->hasOneUse() || (Inst->user_back() != Insert1 && Inst->user_back() != Insert2)) + return BB; + } + return nullptr; + } + + // Look for the landingpad prolog: + // %lpad_vals = landingpad ... + // [%0 = extractvalue { i8*, i32 } %lpad_vals, 0] + // [store i8* %0, i8** %exn.slot] + // %1 = extractvalue { i8*, i32 } %lpad_vals, 1 + // [store i32 %1, i32* %ehselector.slot] + // [call void @llvm.eh.endcatch()] + // br label %dispatch + // or + // %lpad_vals = landingpad ... + // [%0 = extractvalue { i8*, i32 } %lpad_vals, 0] + // [store i8* %0, i8** %exn.slot] + // %1 = extractvalue { i8*, i32 } %lpad_vals, 1 + // [store i32 %1, i32* %ehselector.slot] + // %2 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) + // %matches = icmp eq i32 %1, %2 + // [call void @llvm.eh.endcatch()] + // br i1 %matches, label %catch, label %eh.resume + if (BB->isLandingPad()) { + LandingPadInst *LPad = BB->getLandingPadInst(); + BranchInst *Branch = dyn_cast(Terminator); + CmpInst *Compare = nullptr; + if (Branch && Branch->isConditional()) + Compare = dyn_cast(Branch->getCondition()); + for (BasicBlock::iterator II = BB->begin(), IE = BB->end(); II != IE; ++II) { + Instruction *Inst = II; + if (isa(Inst) || isa(Inst)) + continue; + if (Inst == LPad || Inst == Compare || Inst == Branch) + continue; + ExtractValueInst *Extract = dyn_cast(Inst); + if (Extract && Extract->getAggregateOperand() == LPad) + continue; + if (auto *Store = dyn_cast(Inst)) { + Extract = dyn_cast(Store->getValueOperand()); + if (Extract && Extract->getAggregateOperand() == LPad) + continue; + } + if (match(Inst, m_Intrinsic())) + continue; + if (match(Inst, m_Intrinsic())) + continue; + // If this instruction is a load used for the branch condition, it's OK + if (Branch && Compare && isa(Inst) && Inst->hasOneUse() && (Inst->user_back() == Compare)) + continue; + // Anything else makes this interesting cleanup code. + return BB; + } + if (BB == EndBB) + return nullptr; + // FIXME: Handle reversed conditions. + BB = Branch->getSuccessor(0); + continue; + } + + BranchInst *Branch = dyn_cast(Terminator); + if (Branch) { + if (Branch->isConditional()) { + // Look for the selector dispatch. + // %sel = load i32* %ehselector.slot + // %2 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIf to i8*)) #4 + // %matches = icmp eq i32 %sel12, %2 + // br i1 %matches, label %catch14, label %eh.resume + CmpInst *Compare = dyn_cast(Branch->getCondition()); + if (!Compare || !Compare->isEquality()) + return BB; + for (BasicBlock::iterator II = BB->begin(), IE = BB->end(); II != IE; ++II) { + Instruction *Inst = II; + if (Inst == Compare || Inst == Branch) + continue; + if (isa(Inst) || isa(Inst)) + continue; + if (!Inst->hasOneUse() || (Inst->user_back() != Compare)) + return BB; + if (match(Inst, m_Intrinsic())) + continue; + if (!isa(Inst)) + return BB; + } + if (BB == EndBB) + return nullptr; + // FIXME: Handle reversed conditions. + BB = Branch->getSuccessor(0); + continue; + } + else { + // Look for empty blocks with unconditional branches. + for (BasicBlock::iterator II = BB->begin(), IE = BB->end(); II != IE; ++II) { + Instruction *Inst = II; + if (isa(Inst) || isa(Inst)) + continue; + if (Inst == Branch) + continue; + IntrinsicInst *IntrinCall = dyn_cast(Inst); + if (IntrinCall && IntrinCall->getIntrinsicID() == Intrinsic::eh_endcatch) + continue; + if (match(Inst, m_Intrinsic())) + continue; + // Anything else makes this interesting cleanup code. + return BB; + } + if (BB == EndBB) + return nullptr; + // The branch was unconditional. + BB = Branch->getSuccessor(0); + continue; + } // End else of if branch was conditional + } // End if Branch + + // Anything else makes this interesting cleanup code. + return BB; + } return nullptr; } Index: lib/Transforms/Utils/CloneFunction.cpp =================================================================== --- lib/Transforms/Utils/CloneFunction.cpp +++ lib/Transforms/Utils/CloneFunction.cpp @@ -397,6 +397,14 @@ // terminator into the new basic block in this case. if (Action == CloningDirector::StopCloningBB) return; + if (Action == CloningDirector::CloneSuccessors) { + // If the director says to skip with a terminate instruction, we still + // need to clone this block's successors. + const TerminatorInst *TI = BB->getTerminator(); + for (unsigned i = 0, e = TI->getNumSuccessors(); i != e; ++i) + ToClone.push_back(TI->getSuccessor(i)); + return; + } assert(Action != CloningDirector::SkipInstruction && "SkipInstruction is not valid for terminators."); } Index: test/CodeGen/X86/cppeh-catch-all.ll =================================================================== --- test/CodeGen/X86/cppeh-catch-all.ll +++ test/CodeGen/X86/cppeh-catch-all.ll @@ -53,7 +53,7 @@ } ; CHECK: define i8* @_Z4testv.catch(i8*, i8*) { -; CHECK: catch.entry: +; CHECK: entry: ; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) ; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* ; CHECK: %eh.obj.ptr = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 1 Index: test/CodeGen/X86/cppeh-catch-scalar.ll =================================================================== --- test/CodeGen/X86/cppeh-catch-scalar.ll +++ test/CodeGen/X86/cppeh-catch-scalar.ll @@ -19,19 +19,19 @@ target triple = "x86_64-pc-windows-msvc" ; This is the structure that will get created for the frame allocation. -; CHECK: %struct._Z4testv.ehdata = type { i32, i8*, i32 } +; CHECK: %struct._Z4testv.ehdata = type { i32, i8* } @_ZTIi = external constant i8* ; The function entry will be rewritten like this. ; CHECK: define void @_Z4testv() #0 { ; CHECK: entry: -; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 24) +; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 16) ; CHECK: %eh.data = bitcast i8* %frame.alloc to %struct._Z4testv.ehdata* ; CHECK: %exn.slot = alloca i8* ; CHECK: %ehselector.slot = alloca i32 ; CHECK-NOT: %i = alloca i32, align 4 -; CHECK: %i = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; CHECK-NOT: %i = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 ; Function Attrs: uwtable define void @_Z4testv() #0 { @@ -86,12 +86,12 @@ } ; CHECK: define i8* @_Z4testv.catch(i8*, i8*) { -; CHECK: catch.entry: +; CHECK: entry: ; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) ; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* ; CHECK: %eh.obj.ptr = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 1 ; CHECK: %eh.obj = load i8** %eh.obj.ptr -; CHECK: %i = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; CHECK: %i = alloca i32, align 4 ; CHECK: %2 = bitcast i8* %eh.obj to i32* ; CHECK: %3 = load i32* %2, align 4 ; CHECK: store i32 %3, i32* %i, align 4 Index: test/CodeGen/X86/cppeh-frame-vars.ll =================================================================== --- test/CodeGen/X86/cppeh-frame-vars.ll +++ test/CodeGen/X86/cppeh-frame-vars.ll @@ -42,7 +42,7 @@ %struct.SomeData = type { i32, i32 } ; This structure should be declared for the frame allocation block. -; CHECK: %"struct.\01?test@@YAXXZ.ehdata" = type { i32, i8*, i32, i32, [10 x i32], i32, %struct.SomeData } +; CHECK: %"struct.\01?test@@YAXXZ.ehdata" = type { i32, i8*, i32, [10 x i32], i32, %struct.SomeData } $"\01??_R0H@8" = comdat any @@ -52,19 +52,19 @@ ; The function entry should be rewritten like this. ; CHECK: define void @"\01?test@@YAXXZ"() #0 { ; CHECK: entry: -; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 80) +; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 72) ; CHECK: %eh.data = bitcast i8* %frame.alloc to %"struct.\01?test@@YAXXZ.ehdata"* ; CHECK-NOT: %NumExceptions = alloca i32, align 4 -; CHECK: %NumExceptions = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 +; CHECK: %NumExceptions = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 ; CHECK-NOT: %ExceptionVal = alloca [10 x i32], align 16 -; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 +; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 ; CHECK-NOT: %Data = alloca %struct.SomeData, align 4 -; CHECK: %Data = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 -; CHECK: %i = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 +; CHECK: %Data = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 +; CHECK: %i = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 ; CHECK: %exn.slot = alloca i8* ; CHECK: %ehselector.slot = alloca i32 ; CHECK-NOT: %e = alloca i32, align 4 -; CHECK: %e = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 +; CHECK-NOT: %e = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 ; Function Attrs: uwtable define void @"\01?test@@YAXXZ"() #0 { @@ -180,16 +180,16 @@ ; The following catch handler should be outlined. ; CHECK: define i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) { -; CHECK: catch.entry: +; CHECK: entry: ; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1) ; CHECK: %eh.data = bitcast i8* %eh.alloc to %"struct.\01?test@@YAXXZ.ehdata"* ; CHECK: %eh.obj.ptr = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 1 ; CHECK: %eh.obj = load i8** %eh.obj.ptr -; CHECK: %e = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 -; CHECK: %NumExceptions = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 -; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 -; CHECK: %i = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 -; CHECK: %Data = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 +; CHECK: %e = alloca i32, align 4 +; CHECK: %NumExceptions = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 +; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 +; CHECK: %i = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 +; CHECK: %Data = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 ; CHECK: %2 = bitcast i8* %eh.obj to i32* ; CHECK: %3 = load i32* %2, align 4 ; CHECK: store i32 %3, i32* %e, align 4 @@ -206,7 +206,7 @@ ; CHECK: %cmp1 = icmp eq i32 %7, %8 ; CHECK: br i1 %cmp1, label %if.then, label %if.else ; -; CHECK: if.then: ; preds = %catch.entry +; CHECK: if.then: ; preds = %entry ; CHECK: %9 = load i32* %e, align 4 ; CHECK: %b = getelementptr inbounds %struct.SomeData* %Data, i32 0, i32 1 ; CHECK: %10 = load i32* %b, align 4 @@ -214,7 +214,7 @@ ; CHECK: store i32 %add2, i32* %b, align 4 ; CHECK: br label %if.end ; -; CHECK: if.else: ; preds = %catch.entry +; CHECK: if.else: ; preds = %entry ; CHECK: %11 = load i32* %e, align 4 ; CHECK: %a3 = getelementptr inbounds %struct.SomeData* %Data, i32 0, i32 0 ; CHECK: %12 = load i32* %a3, align 4 Index: test/CodeGen/X86/cppeh-min-unwind.ll =================================================================== --- test/CodeGen/X86/cppeh-min-unwind.ll +++ test/CodeGen/X86/cppeh-min-unwind.ll @@ -0,0 +1,94 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test was generated from the following source: +; +; class SomeClass { +; public: +; SomeClass(); +; ~SomeClass(); +; }; +; +; void test() { +; SomeClass obj; +; may_throw(); +; } + + +; ModuleID = 'min-unwind.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; This structure should be created for the frame allocation. +; CHECK: %struct._Z4testv.ehdata = type { i32, i8*, %class.SomeClass } + +%class.SomeClass = type { [28 x i32] } + +; The function entry should be rewritten like this. +; CHECK: define void @_Z4testv() #0 { +; CHECK: entry: +; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 128) +; CHECK: %eh.data = bitcast i8* %frame.alloc to %struct._Z4testv.ehdata* +; CHECK-NOT: %obj = alloca %class.SomeClass, align 4 +; CHECK: %obj = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 + +; Function Attrs: uwtable +define void @_Z4testv() #0 { +entry: + %obj = alloca %class.SomeClass, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + call void @_ZN9SomeClassC1Ev(%class.SomeClass* %obj) + invoke void @_Z9may_throwv() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + call void @_ZN9SomeClassD1Ev(%class.SomeClass* %obj) + ret void + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %1 = extractvalue { i8*, i32 } %0, 0 + store i8* %1, i8** %exn.slot + %2 = extractvalue { i8*, i32 } %0, 1 + store i32 %2, i32* %ehselector.slot + call void @_ZN9SomeClassD1Ev(%class.SomeClass* %obj) + br label %eh.resume + +eh.resume: ; preds = %lpad + %exn = load i8** %exn.slot + %sel = load i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0 + %lpad.val2 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1 + resume { i8*, i32 } %lpad.val2 +} + +; This cleanup handler should be outlined. +; CHECK: define void @_Z4testv.cleanup(i8*, i8*) { +; CHECK: entry: +; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) +; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* +; CHECK: %obj = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; CHECK: call void @_ZN9SomeClassD1Ev(%class.SomeClass* %obj) +; CHECK: ret void +; CHECK: } + +declare void @_ZN9SomeClassC1Ev(%class.SomeClass*) #1 + +declare void @_Z9may_throwv() #1 + +declare i32 @__CxxFrameHandler3(...) + +declare void @_ZN9SomeClassD1Ev(%class.SomeClass*) #1 + +declare i8* @llvm.eh.begincatch(i8*) + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { noinline noreturn nounwind } +attributes #3 = { noreturn nounwind } +attributes #4 = { nounwind } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 3.7.0 (trunk 226027)"} Index: test/CodeGen/X86/cppeh-nested-2.ll =================================================================== --- test/CodeGen/X86/cppeh-nested-2.ll +++ test/CodeGen/X86/cppeh-nested-2.ll @@ -0,0 +1,395 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; class Inner { +; public: +; Inner(); +; ~Inner(); +; }; +; class Outer { +; public: +; Outer(); +; ~Outer(); +; }; +; void test() { +; try { +; Outer outer; +; try { +; Inner inner; +; may_throw(); +; } catch (int i) { +; handle_int(i); +; } +; } catch (float f) { +; handle_float(f); +; } +; done(); +; } + +; ModuleID = 'nested-2.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; This structure should be created for the frame allocation. +; CHECK: %struct._Z4testv.ehdata = type { i32, i8*, %class.Outer, %class.Inner } + +%class.Outer = type { i8 } +%class.Inner = type { i8 } + +@_ZTIf = external constant i8* +@_ZTIi = external constant i8* + +; The function entry should be rewritten like this. +; CHECK: define void @_Z4testv() #0 { +; CHECK: entry: +; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 24) +; CHECK: %eh.data = bitcast i8* %frame.alloc to %struct._Z4testv.ehdata* +; CHECK-NOT: %outer = alloca %class.Outer, align 1 +; CHECK: %outer = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; (FIXME -- This should be eliminated, but it isn't yet.) %exn.slot = alloca i8* +; (FIXME -- This should be eliminated, but it isn't yet.) %ehselector.slot = alloca i32 +; CHECK-NOT: %inner = alloca %class.Inner, align 1 +; CHECK: %inner = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 3 + +; Function Attrs: uwtable +define void @_Z4testv() #0 { +entry: + %outer = alloca %class.Outer, align 1 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %inner = alloca %class.Inner, align 1 + %i = alloca i32, align 4 + %f = alloca float, align 4 + invoke void @_ZN5OuterC1Ev(%class.Outer* %outer) + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + invoke void @_ZN5InnerC1Ev(%class.Inner* %inner) + to label %invoke.cont2 unwind label %lpad1 + +invoke.cont2: ; preds = %invoke.cont + invoke void @_Z9may_throwv() + to label %invoke.cont4 unwind label %lpad3 + +invoke.cont4: ; preds = %invoke.cont2 + invoke void @_ZN5InnerD1Ev(%class.Inner* %inner) + to label %invoke.cont5 unwind label %lpad1 + +invoke.cont5: ; preds = %invoke.cont4 + br label %try.cont + +; CHECK: lpad: ; preds = %try.cont, %entry +; CHECK: %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: catch i8* bitcast (i8** @_ZTIf to i8*) +; CHECK: %recover = call i8* (...)* @llvm.eh.actions({ i8*, i32 } %0) +; CHECK: indirectbr i8* %recover, [] + +lpad: ; preds = %try.cont, %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (i8** @_ZTIf to i8*) + %1 = extractvalue { i8*, i32 } %0, 0 + store i8* %1, i8** %exn.slot + %2 = extractvalue { i8*, i32 } %0, 1 + store i32 %2, i32* %ehselector.slot + br label %catch.dispatch11 + +; CHECK: lpad1: ; preds = %invoke.cont4, %invoke.cont +; CHECK: %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: cleanup +; CHECK: catch i8* bitcast (i8** @_ZTIi to i8*) +; CHECK: catch i8* bitcast (i8** @_ZTIf to i8*) +; CHECK: %recover1 = call i8* (...)* @llvm.eh.actions({ i8*, i32 } %1) +; CHECK: indirectbr i8* %recover1, [] + +lpad1: ; preds = %invoke.cont4, %invoke.cont + %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (i8** @_ZTIi to i8*) + catch i8* bitcast (i8** @_ZTIf to i8*) + %4 = extractvalue { i8*, i32 } %3, 0 + store i8* %4, i8** %exn.slot + %5 = extractvalue { i8*, i32 } %3, 1 + store i32 %5, i32* %ehselector.slot + br label %catch.dispatch + +; CHECK: lpad3: ; preds = %invoke.cont2 +; CHECK: %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: cleanup +; CHECK: catch i8* bitcast (i8** @_ZTIi to i8*) +; CHECK: catch i8* bitcast (i8** @_ZTIf to i8*) +; CHECK-NOT: %7 = extractvalue { i8*, i32 } %6, 0 +; CHECK-NOT: store i8* %7, i8** %exn.slot +; CHECK-NOT: %8 = extractvalue { i8*, i32 } %6, 1 +; CHECK-NOT: store i32 %8, i32* %ehselector.slot +; CHECK-NOT: call void @_ZN5InnerD1Ev(%class.Inner* %inner) +; CHECK: %recover2 = call i8* (...)* @llvm.eh.actions({ i8*, i32 } %2) +; CHECK: indirectbr i8* %recover2, [] + +lpad3: ; preds = %invoke.cont2 + %6 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (i8** @_ZTIi to i8*) + catch i8* bitcast (i8** @_ZTIf to i8*) + %7 = extractvalue { i8*, i32 } %6, 0 + store i8* %7, i8** %exn.slot + %8 = extractvalue { i8*, i32 } %6, 1 + store i32 %8, i32* %ehselector.slot + call void @_ZN5InnerD1Ev(%class.Inner* %inner) + br label %catch.dispatch + +; CHECK-NOT: catch.dispatch: ; preds = %lpad3, %lpad1 +; CHECK-NOT: %sel = load i32* %ehselector.slot +; CHECK-NOT: %9 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #4 +; CHECK-NOT: %matches = icmp eq i32 %sel, %9 +; CHECK-NOT: br i1 %matches, label %catch, label %ehcleanup + +catch.dispatch: ; preds = %lpad3, %lpad1 + %sel = load i32* %ehselector.slot + %9 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #4 + %matches = icmp eq i32 %sel, %9 + br i1 %matches, label %catch, label %ehcleanup + +; CHECK-NOT: catch: ; preds = %catch.dispatch +; CHECK-NOT: %exn = load i8** %exn.slot +; CHECK-NOT: %10 = call i8* @llvm.eh.begincatch(i8* %exn) #4 +; CHECK-NOT: %11 = bitcast i8* %10 to i32* +; CHECK-NOT: %12 = load i32* %11, align 4 +; CHECK-NOT: store i32 %12, i32* %i, align 4 +; CHECK-NOT: %13 = load i32* %i, align 4 +; CHECK-NOT: invoke void @_Z10handle_inti(i32 %13) +; CHECK-NOT: to label %invoke.cont8 unwind label %lpad7 + +catch: ; preds = %catch.dispatch + %exn = load i8** %exn.slot + %10 = call i8* @llvm.eh.begincatch(i8* %exn) #4 + %11 = bitcast i8* %10 to i32* + %12 = load i32* %11, align 4 + store i32 %12, i32* %i, align 4 + %13 = load i32* %i, align 4 + invoke void @_Z10handle_inti(i32 %13) + to label %invoke.cont8 unwind label %lpad7 + +invoke.cont8: ; preds = %catch + call void @llvm.eh.endcatch() #4 + br label %try.cont + +try.cont: ; preds = %invoke.cont8, %invoke.cont5 + invoke void @_ZN5OuterD1Ev(%class.Outer* %outer) + to label %invoke.cont9 unwind label %lpad + +; We need to check this here so that we can verify that an identical branch +; instruction is not present later. +; CHECK: invoke.cont9: ; preds = %try.cont +; CHECK: br label %try.cont19 + +invoke.cont9: ; preds = %try.cont + br label %try.cont19 + +; CHECK-NOT: lpad7: ; preds = %catch +; CHECK-NOT: %14 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NOT: cleanup +; CHECK-NOT: catch i8* bitcast (i8** @_ZTIf to i8*) +; CHECK-NOT: %15 = extractvalue { i8*, i32 } %14, 0 +; CHECK-NOT: store i8* %15, i8** %exn.slot +; CHECK-NOT: %16 = extractvalue { i8*, i32 } %14, 1 +; CHECK-NOT: store i32 %16, i32* %ehselector.slot +; CHECK-NOT: call void @llvm.eh.endcatch() #4 +; CHECK-NOT: br label %ehcleanup + +lpad7: ; preds = %catch + %14 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (i8** @_ZTIf to i8*) + %15 = extractvalue { i8*, i32 } %14, 0 + store i8* %15, i8** %exn.slot + %16 = extractvalue { i8*, i32 } %14, 1 + store i32 %16, i32* %ehselector.slot + call void @llvm.eh.endcatch() #4 + br label %ehcleanup + +; CHECK-NOT: ehcleanup: ; preds = %lpad7, %catch.dispatch +; CHECK-NOT: call void @_ZN5OuterD1Ev(%class.Outer* %outer) +; CHECK-NOT: br label %catch.dispatch11 + +ehcleanup: ; preds = %lpad7, %catch.dispatch + call void @_ZN5OuterD1Ev(%class.Outer* %outer) + br label %catch.dispatch11 + +; CHECK-NOT: catch.dispatch11: ; preds = %ehcleanup, %lpad +; CHECK-NOT: %sel12 = load i32* %ehselector.slot +; CHECK-NOT: %17 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIf to i8*)) #4 +; CHECK-NOT: %matches13 = icmp eq i32 %sel12, %17 +; CHECK-NOT: br i1 %matches13, label %catch14, label %eh.resume + +catch.dispatch11: ; preds = %ehcleanup, %lpad + %sel12 = load i32* %ehselector.slot + %17 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIf to i8*)) #4 + %matches13 = icmp eq i32 %sel12, %17 + br i1 %matches13, label %catch14, label %eh.resume + +; CHECK-NOT: catch14: ; preds = %catch.dispatch11 +; CHECK-NOT: %exn15 = load i8** %exn.slot +; CHECK-NOT: %18 = call i8* @llvm.eh.begincatch(i8* %exn15) #4 +; CHECK-NOT: %19 = bitcast i8* %18 to float* +; CHECK-NOT: %20 = load float* %19, align 4 +; CHECK-NOT: store float %20, float* %f, align 4 +; CHECK-NOT: %21 = load float* %f, align 4 +; CHECK-NOT: invoke void @_Z12handle_floatf(float %21) +; CHECK-NOT: to label %invoke.cont17 unwind label %lpad16 + +catch14: ; preds = %catch.dispatch11 + %exn15 = load i8** %exn.slot + %18 = call i8* @llvm.eh.begincatch(i8* %exn15) #4 + %19 = bitcast i8* %18 to float* + %20 = load float* %19, align 4 + store float %20, float* %f, align 4 + %21 = load float* %f, align 4 + invoke void @_Z12handle_floatf(float %21) + to label %invoke.cont17 unwind label %lpad16 + +; CHECK-NOT: invoke.cont17: ; preds = %catch14 +; CHECK-NOT: call void @llvm.eh.endcatch() #4 +; CHECK-NOT: br label %try.cont19 + +invoke.cont17: ; preds = %catch14 + call void @llvm.eh.endcatch() #4 + br label %try.cont19 + +try.cont19: ; preds = %invoke.cont17, %invoke.cont9 + call void @_Z4donev() + ret void + +; CHECK-NOT: lpad16: ; preds = %catch14 +; CHECK-NOT: %22 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NOT: cleanup +; CHECK-NOT: %23 = extractvalue { i8*, i32 } %22, 0 +; CHECK-NOT: store i8* %23, i8** %exn.slot +; CHECK-NOT: %24 = extractvalue { i8*, i32 } %22, 1 +; CHECK-NOT: store i32 %24, i32* %ehselector.slot +; CHECK-NOT: call void @llvm.eh.endcatch() #4 +; CHECK-NOT: br label %eh.resume + +lpad16: ; preds = %catch14 + %22 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %23 = extractvalue { i8*, i32 } %22, 0 + store i8* %23, i8** %exn.slot + %24 = extractvalue { i8*, i32 } %22, 1 + store i32 %24, i32* %ehselector.slot + call void @llvm.eh.endcatch() #4 + br label %eh.resume + +; CHECK-NOT: eh.resume: ; preds = %lpad16, %catch.dispatch11 +; CHECK-NOT: %exn20 = load i8** %exn.slot +; CHECK-NOT: %sel21 = load i32* %ehselector.slot +; CHECK-NOT: %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn20, 0 +; CHECK-NOT: %lpad.val22 = insertvalue { i8*, i32 } %lpad.val, i32 %sel21, 1 +; CHECK-NOT: resume { i8*, i32 } %lpad.val22 + +eh.resume: ; preds = %lpad16, %catch.dispatch11 + %exn20 = load i8** %exn.slot + %sel21 = load i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn20, 0 + %lpad.val22 = insertvalue { i8*, i32 } %lpad.val, i32 %sel21, 1 + resume { i8*, i32 } %lpad.val22 + +; CHECK: } +} + +; This catch handler should be outlined. +; CHECK: define i8* @_Z4testv.catch(i8*, i8*) { +; CHECK: entry: +; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) +; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* +; CHECK: %eh.obj.ptr = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 1 +; CHECK: %eh.obj = load i8** %eh.obj.ptr +; CHECK: %f = alloca float +; CHECK: %2 = bitcast i8* %eh.obj to float* +; CHECK: %3 = load float* %2, align 4 +; CHECK: store float %3, float* %f, align 4 +; CHECK: %4 = load float* %f, align 4 +; CHECK: call void @_Z12handle_floatf(float %4) +; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont19) +; CHECK: } + +; This catch handler should be outlined. +; CHECK: define i8* @_Z4testv.catch1(i8*, i8*) { +; CHECK: entry: +; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) +; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* +; CHECK: %eh.obj.ptr = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 1 +; CHECK: %eh.obj = load i8** %eh.obj.ptr +; CHECK: %i = alloca i32 +; CHECK: %2 = bitcast i8* %eh.obj to i32* +; CHECK: %3 = load i32* %2, align 4 +; CHECK: store i32 %3, i32* %i, align 4 +; CHECK: %4 = load i32* %i, align 4 +; CHECK: invoke void @_Z10handle_inti(i32 %4) +; CHECK: to label %invoke.cont8 unwind label %lpad7 +; +; CHECK: invoke.cont8: ; preds = %entry +; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont) +; +; CHECK: lpad7: ; preds = %entry +; CHECK: %5 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; (FIXME) The nested handler body isn't being populated yet. +; CHECK: } + +; This cleanup handler should be outlined. +; CHECK: define void @_Z4testv.cleanup(i8*, i8*) { +; CHECK: entry: +; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) +; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* +; CHECK: %outer = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 +; CHECK: call void @_ZN5OuterD1Ev(%class.Outer* %outer) +; CHECK: ret void +; CHECK: } + +; This cleanup handler should be outlined. +; CHECK: define void @_Z4testv.cleanup2(i8*, i8*) { +; CHECK: entry: +; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) +; CHECK: %eh.data = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* +; CHECK: %inner = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 3 +; CHECK: call void @_ZN5InnerD1Ev(%class.Inner* %inner) +; CHECK: ret void +; CHECK: } + + + +declare void @_ZN5OuterC1Ev(%class.Outer*) #1 + +declare i32 @__CxxFrameHandler3(...) + +declare void @_ZN5InnerC1Ev(%class.Inner*) #1 + +declare void @_Z9may_throwv() #1 + +declare void @_ZN5InnerD1Ev(%class.Inner*) #1 + +declare i8* @llvm.eh.begincatch(i8*) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #3 + +declare void @_Z10handle_inti(i32) #1 + +declare void @llvm.eh.endcatch() + +declare void @_ZN5OuterD1Ev(%class.Outer*) #1 + +declare void @_Z12handle_floatf(float) #1 + +declare void @_Z4donev() #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { noinline noreturn nounwind } +attributes #3 = { nounwind readnone } +attributes #4 = { nounwind } +attributes #5 = { noreturn nounwind } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 3.7.0 (trunk 226027)"} Index: test/CodeGen/X86/cppeh-nonalloca-frame-values.ll =================================================================== --- test/CodeGen/X86/cppeh-nonalloca-frame-values.ll +++ test/CodeGen/X86/cppeh-nonalloca-frame-values.ll @@ -0,0 +1,263 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; struct SomeData { +; int a; +; int b; +; }; +; +; void may_throw(); +; void does_not_throw(int i); +; void dump(int *, int, SomeData&); +; +; void test() { +; int NumExceptions = 0; +; int ExceptionVal[10]; +; SomeData Data = { 0, 0 }; +; +; for (int i = 0; i < 10; ++i) { +; try { +; may_throw(); +; Data.a += i; +; } +; catch (int e) { +; ExceptionVal[NumExceptions] = e; +; ++NumExceptions; +; if (e == i) +; Data.b += e; +; else +; Data.a += e; +; } +; does_not_throw(NumExceptions); +; } +; dump(ExceptionVal, NumExceptions, Data); +; } +; +; Unlike the cppeh-frame-vars.ll test, this test was generated using -O2 +; optimization, which results in non-alloca values being used in the +; catch handler. + +; ModuleID = 'cppeh-frame-vars.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%struct.SomeData = type { i32, i32 } + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat + +; This structure should be declared for the frame allocation block. +; CHECK: %"struct.\01?test@@YAXXZ.ehdata" = type { i32, i8*, i32, [10 x i32], i32, i32*, i32* } + +; The function entry should be rewritten like this. +; CHECK: define void @"\01?test@@YAXXZ"() #0 { +; CHECK: entry: +; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 80) +; CHECK: %eh.data = bitcast i8* %frame.alloc to %"struct.\01?test@@YAXXZ.ehdata"* +; CHECK-NOT: %ExceptionVal = alloca [10 x i32], align 16 +; CHECK: %NumExceptions.020.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 +; CHECK: %i.019.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 +; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 +; CHECK: %Data = alloca i64, align 8 +; CHECK: %tmpcast = bitcast i64* %Data to %struct.SomeData* +; CHECK: %0 = bitcast [10 x i32]* %ExceptionVal to i8* +; CHECK: call void @llvm.lifetime.start(i64 40, i8* %0) #1 +; CHECK: store i64 0, i64* %Data, align 8 +; CHECK: %a.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 +; CHECK: %a = bitcast i64* %Data to i32* +; CHECK: store i32* %a, i32** %a.reg2mem +; CHECK: %b = getelementptr inbounds %struct.SomeData* %tmpcast, i64 0, i32 1 +; CHECK: %b.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 +; CHECK: store i32* %b, i32** %b.reg2mem +; CHECK: store i32 0, i32* %NumExceptions.020.reg2mem +; CHECK: store i32 0, i32* %i.019.reg2mem +; CHECK: br label %for.body + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %ExceptionVal = alloca [10 x i32], align 16 + %Data = alloca i64, align 8 + %tmpcast = bitcast i64* %Data to %struct.SomeData* + %0 = bitcast [10 x i32]* %ExceptionVal to i8* + call void @llvm.lifetime.start(i64 40, i8* %0) #1 + store i64 0, i64* %Data, align 8 + %a = bitcast i64* %Data to i32* + %b = getelementptr inbounds %struct.SomeData* %tmpcast, i64 0, i32 1 + br label %for.body + +; CHECK: for.body: +; CHECK-NOT: %NumExceptions.020 = phi i32 [ 0, %entry ], [ %NumExceptions.1, %try.cont ] +; CHECK-NOT: %i.019 = phi i32 [ 0, %entry ], [ %inc5, %try.cont ] +; CHECK: %i.019.reload = load i32* %i.019.reg2mem +; CHECK: %NumExceptions.020.reload = load i32* %NumExceptions.020.reg2mem +for.body: ; preds = %entry, %try.cont + %NumExceptions.020 = phi i32 [ 0, %entry ], [ %NumExceptions.1, %try.cont ] + %i.019 = phi i32 [ 0, %entry ], [ %inc5, %try.cont ] + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +; CHECK: invoke.cont: ; preds = %for.body +; CHECK-NOT: %1 = load i32* %a, align 8, !tbaa !2 +; CHECK-NOT: %add = add nsw i32 %1, %i.019 +; CHECK-NOT: store i32 %add, i32* %a, align 8, !tbaa !2 +; CHECK: %a.reload1 = load volatile i32** %a.reg2mem +; CHECK: %1 = load i32* %a.reload1, align 8, !tbaa !2 +; CHECK: %add = add nsw i32 %1, %i.019.reload +; CHECK: %a.reload = load volatile i32** %a.reg2mem +; CHECK: store i32 %add, i32* %a.reload, align 8, !tbaa !2 +; CHECK: br label %try.cont +invoke.cont: ; preds = %for.body + %1 = load i32* %a, align 8, !tbaa !2 + %add = add nsw i32 %1, %i.019 + store i32 %add, i32* %a, align 8, !tbaa !2 + br label %try.cont + +lpad: ; preds = %for.body + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %3 = extractvalue { i8*, i32 } %2, 1 + %4 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #1 + %matches = icmp eq i32 %3, %4 + br i1 %matches, label %catch, label %eh.resume + +catch: ; preds = %lpad + %5 = extractvalue { i8*, i32 } %2, 0 + %6 = tail call i8* @llvm.eh.begincatch(i8* %5) #1 + %7 = bitcast i8* %6 to i32* + %8 = load i32* %7, align 4, !tbaa !7 + %idxprom = sext i32 %NumExceptions.020 to i64 + %arrayidx = getelementptr inbounds [10 x i32]* %ExceptionVal, i64 0, i64 %idxprom + store i32 %8, i32* %arrayidx, align 4, !tbaa !7 + %inc = add nsw i32 %NumExceptions.020, 1 + %cmp1 = icmp eq i32 %8, %i.019 + br i1 %cmp1, label %if.then, label %if.else + +if.then: ; preds = %catch + %9 = load i32* %b, align 4, !tbaa !8 + %add2 = add nsw i32 %9, %i.019 + store i32 %add2, i32* %b, align 4, !tbaa !8 + br label %if.end + +if.else: ; preds = %catch + %10 = load i32* %a, align 8, !tbaa !2 + %add4 = add nsw i32 %10, %8 + store i32 %add4, i32* %a, align 8, !tbaa !2 + br label %if.end + +if.end: ; preds = %if.else, %if.then + tail call void @llvm.eh.endcatch() #1 + br label %try.cont + +; CHECK: try.cont: ; preds = %invoke.cont +; CHECK-NOT: %NumExceptions.1 = phi i32 [ %NumExceptions.020, %invoke.cont ], [ %inc, %if.end ] +; CHECK: tail call void @"\01?does_not_throw@@YAXH@Z"(i32 %NumExceptions.020.reload) +; CHECK-NOT: %inc5 = add nuw nsw i32 %i.019, 1 +; CHECK: %inc5 = add nuw nsw i32 %i.019.reload, 1 +; CHECK: %cmp = icmp slt i32 %inc5, 10 +; CHECK: store i32 %NumExceptions.020.reload, i32* %NumExceptions.020.reg2mem +; CHECK: store i32 %inc5, i32* %i.019.reg2mem +; CHECK: br i1 %cmp, label %for.body, label %for.end + +try.cont: ; preds = %if.end, %invoke.cont + %NumExceptions.1 = phi i32 [ %NumExceptions.020, %invoke.cont ], [ %inc, %if.end ] + tail call void @"\01?does_not_throw@@YAXH@Z"(i32 %NumExceptions.1) + %inc5 = add nuw nsw i32 %i.019, 1 + %cmp = icmp slt i32 %inc5, 10 + br i1 %cmp, label %for.body, label %for.end + +for.end: ; preds = %try.cont + %NumExceptions.1.lcssa = phi i32 [ %NumExceptions.1, %try.cont ] + %arraydecay = getelementptr inbounds [10 x i32]* %ExceptionVal, i64 0, i64 0 + call void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32* %arraydecay, i32 %NumExceptions.1.lcssa, %struct.SomeData* dereferenceable(8) %tmpcast) + call void @llvm.lifetime.end(i64 40, i8* %0) #1 + ret void + +eh.resume: ; preds = %lpad + %.lcssa = phi { i8*, i32 } [ %2, %lpad ] + resume { i8*, i32 } %.lcssa +} + +; The following catch handler should be outlined. +; CHECK: define i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) { +; CHECK: entry: +; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1) +; CHECK: %eh.data = bitcast i8* %eh.alloc to %"struct.\01?test@@YAXXZ.ehdata"* +; CHECK: %eh.obj.ptr = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 1 +; CHECK: %eh.obj = load i8** %eh.obj.ptr +; CHECK: %eh.temp.alloca = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 +; CHECK: %NumExceptions.020.reload = load i32* %eh.temp.alloca +; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 +; CHECK: %eh.temp.alloca1 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 +; CHECK: %i.019.reload = load i32* %eh.temp.alloca1 +; CHECK: %eh.temp.alloca2 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 +; CHECK: %a.reload = load i32** %eh.temp.alloca2 +; CHECK: %eh.temp.alloca3 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 +; CHECK: %b.reload = load i32** %eh.temp.alloca3 +; CHECK: %2 = bitcast i8* %eh.obj to i32* +; CHECK: %3 = load i32* %2, align 4, !tbaa !7 +; CHECK: %idxprom = sext i32 %NumExceptions.020.reload to i64 +; CHECK: %arrayidx = getelementptr inbounds [10 x i32]* %ExceptionVal, i64 0, i64 %idxprom +; CHECK: store i32 %3, i32* %arrayidx, align 4, !tbaa !7 +; CHECK: %inc = add nsw i32 %NumExceptions.020.reload, 1 +; CHECK: %cmp1 = icmp eq i32 %3, %i.019.reload +; CHECK: br i1 %cmp1, label %if.then, label %if.else +; +; CHECK: if.then: ; preds = %entry +; CHECK: %4 = load i32* %b.reload, align 4, !tbaa !8 +; CHECK: %add2 = add nsw i32 %4, %i.019.reload +; CHECK: store i32 %add2, i32* %b.reload, align 4, !tbaa !8 +; CHECK: br label %if.end +; +; CHECK: if.else: ; preds = %entry +; CHECK: %5 = load i32* %a.reload, align 8, !tbaa !2 +; CHECK: %add4 = add nsw i32 %5, %3 +; CHECK: store i32 %add4, i32* %a.reload, align 8, !tbaa !2 +; CHECK: br label %if.end +; +; CHECK: if.end: ; preds = %if.else, %if.then +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont) +; CHECK: } + +; Function Attrs: nounwind +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 + +declare void @"\01?may_throw@@YAXXZ"() #2 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #3 + +declare i8* @llvm.eh.begincatch(i8*) + +declare void @llvm.eh.endcatch() + +declare void @"\01?does_not_throw@@YAXH@Z"(i32) #2 + +declare void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32*, i32, %struct.SomeData* dereferenceable(8)) #2 + +; Function Attrs: nounwind +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 228868)"} +!2 = !{!3, !4, i64 0} +!3 = !{!"?AUSomeData@@", !4, i64 0, !4, i64 4} +!4 = !{!"int", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C/C++ TBAA"} +!7 = !{!4, !4, i64 0} +!8 = !{!3, !4, i64 4}