From 83eaade2b42a73685ad4d09ad92edbe001349bb5 Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Tue, 3 Mar 2015 20:00:16 +0000 Subject: [PATCH] Outline cleanup handlers for native Windows C++ exception handling Differential Revision: http://reviews.llvm.org/D7865 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@231117 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/WinEHPrepare.cpp | 348 ++++++++++++++++------- test/CodeGen/X86/cppeh-catch-all.ll | 4 +- test/CodeGen/X86/cppeh-catch-scalar.ll | 4 +- test/CodeGen/X86/cppeh-frame-vars.ll | 8 +- test/CodeGen/X86/cppeh-inalloca.ll | 4 +- test/CodeGen/X86/cppeh-min-unwind.ll | 92 ++++++ test/CodeGen/X86/cppeh-nonalloca-frame-values.ll | 8 +- 7 files changed, 357 insertions(+), 111 deletions(-) create mode 100644 test/CodeGen/X86/cppeh-min-unwind.ll diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index 36f609a05c2..4b22d1c7e14 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -51,6 +51,8 @@ typedef MapVector FrameVarInfoMap; class WinEHPrepare : public FunctionPass { std::unique_ptr DwarfPrepare; + enum HandlerType { Catch, Cleanup }; + public: static char ID; // Pass identification, replacement for typeid. WinEHPrepare(const TargetMachine *TM = nullptr) @@ -69,9 +71,10 @@ public: private: bool prepareCPPEHHandlers(Function &F, SmallVectorImpl &LPads); - bool outlineCatchHandler(Function *SrcFn, Constant *SelectorType, - LandingPadInst *LPad, CallInst *&EHAlloc, - AllocaInst *&EHObjPtr, FrameVarInfoMap &VarInfo); + bool outlineHandler(HandlerType CatchOrCleanup, Function *SrcFn, + Constant *SelectorType, LandingPadInst *LPad, + CallInst *&EHAlloc, AllocaInst *&EHObjPtr, + FrameVarInfoMap &VarInfo); }; class WinEHFrameVariableMaterializer : public ValueMaterializer { @@ -87,12 +90,11 @@ private: 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 +102,23 @@ public: const Instruction *Inst, BasicBlock *NewBB) override; + 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 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,6 +128,48 @@ private: const Value *EHPtrStoreAddr; const Value *SelectorStoreAddr; }; + +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 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 handleResume(ValueToValueMapTy &VMap, const ResumeInst *Resume, + BasicBlock *NewBB) override; +}; + } // end anonymous namespace char WinEHPrepare::ID = 0; @@ -213,8 +268,8 @@ bool WinEHPrepare::prepareCPPEHHandlers( // HandlerData vector. CallInst *EHAlloc = nullptr; AllocaInst *EHObjPtr = nullptr; - bool Outlined = outlineCatchHandler(&F, LPad->getClause(Idx), LPad, - EHAlloc, EHObjPtr, FrameVarInfo); + bool Outlined = outlineHandler(Catch, &F, LPad->getClause(Idx), LPad, + EHAlloc, EHObjPtr, FrameVarInfo); if (Outlined) { HandlersOutlined = true; // These values must be resolved after all handlers have been @@ -226,7 +281,26 @@ bool WinEHPrepare::prepareCPPEHHandlers( } } // End if (isCatch) } // End for each clause - } // End for each landingpad + + // FIXME: This only handles the simple case where there is a 1:1 + // correspondence between landing pad and cleanup blocks. + // It does not handle cases where there are catch blocks between + // cleanup blocks or the case where a cleanup block is shared by + // multiple landing pads. Those cases will be supported later + // when landing pad block analysis is added. + if (LPad->isCleanup()) { + CallInst *EHAlloc = nullptr; + AllocaInst *IgnoreEHObjPtr = nullptr; + bool Outlined = outlineHandler(Cleanup, &F, nullptr, LPad, EHAlloc, + IgnoreEHObjPtr, FrameVarInfo); + if (Outlined) { + HandlersOutlined = true; + // This value must be resolved after all handlers have been outlined. + if (EHAlloc) + HandlerAllocs.push_back(EHAlloc); + } + } + } // End for each landingpad // If nothing got outlined, there is no more processing to be done. if (!HandlersOutlined) @@ -414,10 +488,10 @@ bool WinEHPrepare::prepareCPPEHHandlers( return HandlersOutlined; } -bool WinEHPrepare::outlineCatchHandler(Function *SrcFn, Constant *SelectorType, - LandingPadInst *LPad, CallInst *&EHAlloc, - AllocaInst *&EHObjPtr, - FrameVarInfoMap &VarInfo) { +bool WinEHPrepare::outlineHandler(HandlerType CatchOrCleanup, Function *SrcFn, + Constant *SelectorType, LandingPadInst *LPad, + CallInst *&EHAlloc, AllocaInst *&EHObjPtr, + FrameVarInfoMap &VarInfo) { Module *M = SrcFn->getParent(); LLVMContext &Context = M->getContext(); @@ -426,14 +500,22 @@ bool WinEHPrepare::outlineCatchHandler(Function *SrcFn, Constant *SelectorType, 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::InternalLinkage, + SrcFn->getName() + ".catch", M); + } else { + FunctionType *FnType = + FunctionType::get(Type::getVoidTy(Context), ArgTys, false); + Handler = Function::Create(FnType, GlobalVariable::InternalLinkage, + 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()); @@ -451,36 +533,43 @@ bool WinEHPrepare::outlineCatchHandler(Function *SrcFn, Constant *SelectorType, 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; + + 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)); + } ValueToValueMapTy VMap; // FIXME: Map other values referenced in the filter handler. - WinEHCatchDirector Director(LPad, CatchHandler, SelectorType, EHObj, VarInfo); - SmallVector Returns; ClonedCodeInfo InlinedFunctionInfo; BasicBlock::iterator II = LPad; - 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)); @@ -490,7 +579,7 @@ bool WinEHPrepare::outlineCatchHandler(Function *SrcFn, Constant *SelectorType, return true; } -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)) { @@ -560,67 +649,132 @@ CloningDirector::CloningAction WinEHCatchDirector::handleInstruction( 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; - return CloningDirector::SkipInstruction; - } - 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; + if (auto *Resume = dyn_cast(Inst)) + return handleResume(VMap, Resume, NewBB); - // 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; - } - 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 (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; +} + +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; +} + +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) { + // This causes a replacement that will collapse the landing pad CFG + // to just the cleanup code. + VMap[Inst] = ConstantInt::get(SelectorIDType, 0); + // Tell the caller not to clone this instruction. + return CloningDirector::SkipInstruction; +} + +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; +} + WinEHFrameVariableMaterializer::WinEHFrameVariableMaterializer( Function *OutlinedFn, FrameVarInfoMap &FrameVarInfo) : FrameVarInfo(FrameVarInfo), Builder(OutlinedFn->getContext()) { diff --git a/test/CodeGen/X86/cppeh-catch-all.ll b/test/CodeGen/X86/cppeh-catch-all.ll index 814e97d2edf..c8d54aca7e3 100644 --- a/test/CodeGen/X86/cppeh-catch-all.ll +++ b/test/CodeGen/X86/cppeh-catch-all.ll @@ -52,8 +52,8 @@ try.cont: ; preds = %invoke.cont2, %invo ret void } -; CHECK: define i8* @_Z4testv.catch(i8*, i8*) { -; CHECK: catch.entry: +; CHECK: define internal 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, %struct._Z4testv.ehdata* %eh.data, i32 0, i32 1 diff --git a/test/CodeGen/X86/cppeh-catch-scalar.ll b/test/CodeGen/X86/cppeh-catch-scalar.ll index 9c16a9b09ef..ce3f1b52f31 100644 --- a/test/CodeGen/X86/cppeh-catch-scalar.ll +++ b/test/CodeGen/X86/cppeh-catch-scalar.ll @@ -83,8 +83,8 @@ eh.resume: ; preds = %catch.dispatch resume { i8*, i32 } %lpad.val5 } -; CHECK-LABEL: define i8* @_Z4testv.catch(i8*, i8*) { -; CHECK: catch.entry: +; CHECK: define internal 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, %struct._Z4testv.ehdata* %eh.data, i32 0, i32 1 diff --git a/test/CodeGen/X86/cppeh-frame-vars.ll b/test/CodeGen/X86/cppeh-frame-vars.ll index 471aeed8f73..6d6644084b9 100644 --- a/test/CodeGen/X86/cppeh-frame-vars.ll +++ b/test/CodeGen/X86/cppeh-frame-vars.ll @@ -177,8 +177,8 @@ eh.resume: ; preds = %catch.dispatch } ; The following catch handler should be outlined. -; CHECK-LABEL: define i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) { -; CHECK: catch.entry: +; CHECK-LABEL: define internal 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", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 1 @@ -201,7 +201,7 @@ eh.resume: ; preds = %catch.dispatch ; CHECK: %cmp1 = icmp eq i32 %tmp14, %tmp15 ; CHECK: br i1 %cmp1, label %if.then, label %if.else ; -; CHECK: if.then: ; preds = %catch.entry +; CHECK: if.then: ; preds = %entry ; CHECK: %tmp16 = load i32, i32* %e, align 4 ; CHECK: %b = getelementptr inbounds %struct.SomeData, %struct.SomeData* %Data, i32 0, i32 1 ; CHECK: %tmp17 = load i32, i32* %b, align 4 @@ -209,7 +209,7 @@ eh.resume: ; preds = %catch.dispatch ; 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: %tmp18 = load i32, i32* %e, align 4 ; CHECK: %a3 = getelementptr inbounds %struct.SomeData, %struct.SomeData* %Data, i32 0, i32 0 ; CHECK: %tmp19 = load i32, i32* %a3, align 4 diff --git a/test/CodeGen/X86/cppeh-inalloca.ll b/test/CodeGen/X86/cppeh-inalloca.ll index 61f5a0c0ec6..72175a6e785 100644 --- a/test/CodeGen/X86/cppeh-inalloca.ll +++ b/test/CodeGen/X86/cppeh-inalloca.ll @@ -130,8 +130,8 @@ eh.resume: ; preds = %ehcleanup } ; The following catch handler should be outlined. -; CHECK: define i8* @"\01?test@@YAHUA@@@Z.catch"(i8*, i8*) { -; CHECK: catch.entry: +; CHECK: define internal i8* @"\01?test@@YAHUA@@@Z.catch"(i8*, i8*) { +; CHECK: entry: ; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1) ; CHECK: %eh.data = bitcast i8* %eh.alloc to %"struct.\01?test@@YAHUA@@@Z.ehdata"* ; CHECK: %eh.obj.ptr = getelementptr inbounds %"struct.\01?test@@YAHUA@@@Z.ehdata", %"struct.\01?test@@YAHUA@@@Z.ehdata"* %eh.data, i32 0, i32 1 diff --git a/test/CodeGen/X86/cppeh-min-unwind.ll b/test/CodeGen/X86/cppeh-min-unwind.ll new file mode 100644 index 00000000000..a7b97385e90 --- /dev/null +++ b/test/CodeGen/X86/cppeh-min-unwind.ll @@ -0,0 +1,92 @@ +; 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, %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 + %tmp = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %tmp1 = extractvalue { i8*, i32 } %tmp, 0 + store i8* %tmp1, i8** %exn.slot + %tmp2 = extractvalue { i8*, i32 } %tmp, 1 + store i32 %tmp2, i32* %ehselector.slot + call void @_ZN9SomeClassD1Ev(%class.SomeClass* %obj) + br label %eh.resume + +eh.resume: ; preds = %lpad + %exn = load i8*, i8** %exn.slot + %sel = load i32, 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 internal 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, %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 + +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)"} diff --git a/test/CodeGen/X86/cppeh-nonalloca-frame-values.ll b/test/CodeGen/X86/cppeh-nonalloca-frame-values.ll index 987b264699d..1239f267bac 100644 --- a/test/CodeGen/X86/cppeh-nonalloca-frame-values.ll +++ b/test/CodeGen/X86/cppeh-nonalloca-frame-values.ll @@ -185,8 +185,8 @@ eh.resume: ; preds = %lpad } ; The following catch handler should be outlined. -; CHECK: define i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) { -; CHECK: catch.entry: +; CHECK: define internal 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", %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 1 @@ -210,13 +210,13 @@ eh.resume: ; preds = %lpad ; CHECK: %cmp1 = icmp eq i32 %tmp8, %i.019.reload ; CHECK: br i1 %cmp1, label %if.then, label %if.else ; -; CHECK: if.then: ; preds = %catch.entry +; CHECK: if.then: ; preds = %entry ; CHECK: %tmp9 = load i32, i32* %b.reload, align 4, !tbaa !8 ; CHECK: %add2 = add nsw i32 %tmp9, %i.019.reload ; CHECK: store i32 %add2, i32* %b.reload, align 4, !tbaa !8 ; CHECK: br label %if.end ; -; CHECK: if.else: ; preds = %catch.entry +; CHECK: if.else: ; preds = %entry ; CHECK: %tmp10 = load i32, i32* %a.reload, align 8, !tbaa !2 ; CHECK: %add4 = add nsw i32 %tmp10, %tmp8 ; CHECK: store i32 %add4, i32* %a.reload, align 8, !tbaa !2 -- 2.11.0