Index: include/llvm/Transforms/Utils/Cloning.h =================================================================== --- include/llvm/Transforms/Utils/Cloning.h +++ include/llvm/Transforms/Utils/Cloning.h @@ -153,6 +153,9 @@ virtual CloningAction handleInstruction(ValueToValueMapTy &VMap, const Instruction *Inst, BasicBlock *NewBB) = 0; + + virtual ValueMapTypeRemapper *getTypeRemapper() { return nullptr; } + virtual ValueMaterializer *getValueMaterializer() { return nullptr; } }; void CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc, Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ lib/CodeGen/WinEHPrepare.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "llvm/CodeGen/Passes.h" +#include "llvm/ADT/MapVector.h" #include "llvm/Analysis/LibCallSemantics.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" @@ -33,6 +34,15 @@ #define DEBUG_TYPE "winehprepare" namespace { + +// This map is used to model frame variable usage during outlining, to +// construct a structure type to hold the frame variables in a frame +// 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, int>> + FrameVarInfoMap; + class WinEHPrepare : public FunctionPass { std::unique_ptr DwarfPrepare; @@ -55,13 +65,30 @@ bool prepareCPPEHHandlers(Function &F, SmallVectorImpl &LPads); bool outlineCatchHandler(Function *SrcFn, Constant *SelectorType, - LandingPadInst *LPad, StructType *EHDataStructTy); + LandingPadInst *LPad, CallInst *&EHAlloc, + AllocaInst *&EHObjPtr, FrameVarInfoMap &VarInfo); +}; + +class WinEHFrameVariableMaterializer : public ValueMaterializer { +public: + WinEHFrameVariableMaterializer(Function *OutlinedFn, + FrameVarInfoMap &FrameVarInfo); + ~WinEHFrameVariableMaterializer() {} + + virtual Value *materializeValueFor(Value *V) override; + +private: + Function *OutlinedFn; + FrameVarInfoMap &FrameVarInfo; + IRBuilder<> Builder; }; class WinEHCatchDirector : public CloningDirector { public: - WinEHCatchDirector(LandingPadInst *LPI, Value *Selector, Value *EHObj) + WinEHCatchDirector(LandingPadInst *LPI, Function *CatchFn, Value *Selector, + Value *EHObj, FrameVarInfoMap &VarInfo) : LPI(LPI), CurrentSelector(Selector->stripPointerCasts()), EHObj(EHObj), + Materializer(CatchFn, VarInfo), SelectorIDType(Type::getInt32Ty(LPI->getContext())), Int8PtrType(Type::getInt8PtrTy(LPI->getContext())) {} @@ -69,10 +96,13 @@ const Instruction *Inst, BasicBlock *NewBB) override; + ValueMaterializer *getValueMaterializer() override { return &Materializer; } + private: LandingPadInst *LPI; Value *CurrentSelector; Value *EHObj; + WinEHFrameVariableMaterializer Materializer; Type *SelectorIDType; Type *Int8PtrType; @@ -145,14 +175,13 @@ bool WinEHPrepare::prepareCPPEHHandlers( Function &F, SmallVectorImpl &LPads) { - // FIXME: Find all frame variable references in the handlers - // to populate the structure elements. - SmallVector AllocStructTys; - AllocStructTys.push_back(Type::getInt32Ty(F.getContext())); // EH state - AllocStructTys.push_back(Type::getInt8PtrTy(F.getContext())); // EH object - StructType *EHDataStructTy = - StructType::create(F.getContext(), AllocStructTys, - "struct." + F.getName().str() + ".ehdata"); + // These containers are used to re-map frame variables that are used in + // outlined catch and cleanup handlers. They will be populated as the + // handlers are outlined. + FrameVarInfoMap FrameVarInfo; + SmallVector HandlerAllocs; + SmallVector HandlerEHObjPtrs; + bool HandlersOutlined = false; for (LandingPadInst *LPad : LPads) { @@ -175,18 +204,174 @@ for (unsigned Idx = 0, NumClauses = LPad->getNumClauses(); Idx < NumClauses; ++Idx) { - if (LPad->isCatch(Idx)) - HandlersOutlined = - outlineCatchHandler(&F, LPad->getClause(Idx), LPad, EHDataStructTy); - } // End for each clause - } // End for each landingpad + if (LPad->isCatch(Idx)) { + // Create a new instance of the handler data structure in the + // HandlerData vector. + CallInst *EHAlloc = nullptr; + AllocaInst *EHObjPtr = nullptr; + bool Outlined = outlineCatchHandler(&F, LPad->getClause(Idx), LPad, + EHAlloc, EHObjPtr, FrameVarInfo); + if (Outlined) { + HandlersOutlined = true; + // These values must be resolved after all handlers have been + // outlined. + if (EHAlloc) + HandlerAllocs.push_back(EHAlloc); + if (EHObjPtr) + HandlerEHObjPtrs.push_back(EHObjPtr); + } + } // End if (isCatch) + } // End for each clause + } // 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. + + // Remap the frame variables. + SmallVector StructTys; + StructTys.push_back(Type::getInt32Ty(F.getContext())); // EH state + StructTys.push_back(Type::getInt8PtrTy(F.getContext())); // EH object + + // Start the index at two since we always have the above fields at 0 and 1. + int Idx = 2; + + // Map the alloca instructions to the corresponding index in the + // frame allocation structure. If any alloca is used only in a single + // handler and is not used in the parent frame after outlining, it will + // be assigned an index of -1, meaning the handler can keep its + // "temporary" alloca and the original alloca can be erased from the + // parent function. If we later encounter this alloca in a second + // handler, we will assign it a place in the frame allocation structure + // at that time. Since the instruction replacement doesn't happen until + // all the entries in the HandlerData have been processed this isn't a + // problem. + for (FrameVarInfoMap::iterator I = FrameVarInfo.begin(), + E = FrameVarInfo.end(); + I != E; I++) { + AllocaInst *ParentAlloca = I->first; + std::pair, int> &InfoPair = I->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 || InfoPair.first.size() > 1) { + Type *VarTy = I->first->getAllocatedType(); + StructTys.push_back(VarTy); + InfoPair.second = 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. + InfoPair.second = -1; + } + } + + // Having filled the StructTys vector and assigned an index to each element, + // we can now create the structure. + StructType *EHDataStructTy = StructType::create( + F.getContext(), StructTys, "struct." + F.getName().str() + ".ehdata"); + 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); + uint64_t EHAllocSize = M->getDataLayout()->getTypeAllocSize(EHDataStructTy); + Value *FrameAllocArgs[] = { + ConstantInt::get(Type::getInt32Ty(Context), EHAllocSize)}; + CallInst *FrameAlloc = + Builder.CreateCall(FrameAllocFn, FrameAllocArgs, "frame.alloc"); + + Value *FrameEHData = Builder.CreateBitCast( + FrameAlloc, EHDataStructTy->getPointerTo(), "eh.data"); + + // Now visit each handler that is using the structure and bitcast its EHAlloc + // value to be a pointer to the frame alloc structure. + DenseMap EHDataMap; + for (CallInst *EHAlloc : HandlerAllocs) { + // The EHAlloc has no uses at this time, so we need to just insert the + // cast before the next instruction. There is always a next instruction. + BasicBlock::iterator II = EHAlloc; + ++II; + Builder.SetInsertPoint(cast(II)); + Value *EHData = Builder.CreateBitCast( + EHAlloc, EHDataStructTy->getPointerTo(), "eh.data"); + EHDataMap[EHAlloc->getParent()->getParent()] = EHData; + } + + // Next, replace the place-holder EHObjPtr allocas with GEP instructions + // that pull the EHObjPtr from the frame alloc structure + for (AllocaInst *EHObjPtr : HandlerEHObjPtrs) { + Value *EHData = EHDataMap[EHObjPtr->getParent()->getParent()]; + Value *ElementPtr = Builder.CreateConstInBoundsGEP2_32(EHData, 0, 1); + EHObjPtr->replaceAllUsesWith(ElementPtr); + EHObjPtr->removeFromParent(); + ElementPtr->takeName(EHObjPtr); + delete EHObjPtr; + } + + // 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. + for (FrameVarInfoMap::iterator I = FrameVarInfo.begin(), + E = FrameVarInfo.end(); + I != E; I++) { + AllocaInst *ParentAlloca = I->first; + std::pair, int> &InfoPair = I->second; + int Idx = InfoPair.second; + + // 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. + if (Idx == -1) { + ParentAlloca->eraseFromParent(); + } else { + // Otherwise, we replace the parent alloca and all outlined allocas + // which map to it with GEP instructions. + + // First replace the original alloca. + Builder.SetInsertPoint(ParentAlloca); + Builder.SetCurrentDebugLocation(ParentAlloca->getDebugLoc()); + Value *ElementPtr = + Builder.CreateConstInBoundsGEP2_32(FrameEHData, 0, Idx); + ParentAlloca->replaceAllUsesWith(ElementPtr); + ParentAlloca->removeFromParent(); + ElementPtr->takeName(ParentAlloca); + delete ParentAlloca; + + // Next replace all outlined allocas that are mapped to it. + for (AllocaInst *TempAlloca : InfoPair.first) { + Value *EHData = EHDataMap[TempAlloca->getParent()->getParent()]; + Builder.SetInsertPoint(TempAlloca); + Builder.SetCurrentDebugLocation(TempAlloca->getDebugLoc()); + ElementPtr = Builder.CreateConstInBoundsGEP2_32(EHData, 0, Idx); + TempAlloca->replaceAllUsesWith(ElementPtr); + TempAlloca->removeFromParent(); + ElementPtr->takeName(TempAlloca); + delete TempAlloca; + } + } // end else of if (Idx == -1) + } // End for each FrameVarInfo entry. return HandlersOutlined; } bool WinEHPrepare::outlineCatchHandler(Function *SrcFn, Constant *SelectorType, - LandingPadInst *LPad, - StructType *EHDataStructTy) { + LandingPadInst *LPad, CallInst *&EHAlloc, + AllocaInst *&EHObjPtr, + FrameVarInfoMap &VarInfo) { Module *M = SrcFn->getParent(); LLVMContext &Context = M->getContext(); @@ -221,12 +406,11 @@ Intrinsic::getDeclaration(M, Intrinsic::framerecover); Value *RecoverArgs[] = {Builder.CreateBitCast(SrcFn, Int8PtrType, ""), &(CatchHandler->getArgumentList().back())}; - CallInst *EHAlloc = - Builder.CreateCall(RecoverFrameFn, RecoverArgs, "eh.alloc"); - Value *EHData = - Builder.CreateBitCast(EHAlloc, EHDataStructTy->getPointerTo(), "ehdata"); - Value *EHObjPtr = - Builder.CreateConstInBoundsGEP2_32(EHData, 0, 1, "eh.obj.ptr"); + 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 @@ -240,7 +424,7 @@ // FIXME: Map other values referenced in the filter handler. - WinEHCatchDirector Director(LPad, SelectorType, EHObj); + WinEHCatchDirector Director(LPad, CatchHandler, SelectorType, EHObj, VarInfo); SmallVector Returns; ClonedCodeInfo InlinedFunctionInfo; @@ -271,9 +455,11 @@ "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. + // 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 + // 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 = Inst; } else { @@ -362,10 +548,10 @@ const BranchInst *Branch = dyn_cast(Terminator); assert(Branch && Branch->isUnconditional()); assert(std::next(BasicBlock::const_iterator(IntrinCall)) == - BasicBlock::const_iterator(Branch)); + BasicBlock::const_iterator(Branch)); ReturnInst::Create(NewBB->getContext(), - BlockAddress::get(Branch->getSuccessor(0)), NewBB); + 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 @@ -388,3 +574,50 @@ // Continue with the default cloning behavior. return CloningDirector::CloneInstruction; } + +WinEHFrameVariableMaterializer::WinEHFrameVariableMaterializer( + Function *OutlinedFn, FrameVarInfoMap &FrameVarInfo) + : OutlinedFn(OutlinedFn), FrameVarInfo(FrameVarInfo), + Builder(OutlinedFn->getContext()) { + Builder.SetInsertPoint(&OutlinedFn->getEntryBlock()); + // FIXME: Do something with the FrameVarMapped so that it is shared across the + // function. +} + +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. + AllocaInst *AV = dyn_cast(V); + if (AV) { + AllocaInst *NewAlloca = Builder.CreateAlloca( + AV->getAllocatedType(), AV->getArraySize(), AV->getName()); + FrameVarInfo[AV].first.push_back(NewAlloca); + return NewAlloca; + } + +// FIXME: Do PHI nodes need special handling? + +// FIXME: Are there other cases we can handle better? GEP, ExtractValue, etc. + +// 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; + } +#endif + + return nullptr; +} Index: lib/Transforms/Utils/CloneFunction.cpp =================================================================== --- lib/Transforms/Utils/CloneFunction.cpp +++ lib/Transforms/Utils/CloneFunction.cpp @@ -261,6 +261,8 @@ ClonedCodeInfo *CodeInfo; const DataLayout *DL; CloningDirector *Director; + ValueMapTypeRemapper *TypeMapper; + ValueMaterializer *Materializer; public: PruningFunctionCloner(Function *newFunc, const Function *oldFunc, @@ -274,6 +276,14 @@ VMap(valueMap), ModuleLevelChanges(moduleLevelChanges), NameSuffix(nameSuffix), CodeInfo(codeInfo), DL(DL), Director(Director) { + // These are optional components. The Director may return null. + if (Director) { + TypeMapper = Director->getTypeRemapper(); + Materializer = Director->getValueMaterializer(); + } else { + TypeMapper = nullptr; + Materializer = nullptr; + } } /// CloneBlock - The specified block is found to be reachable, clone it and @@ -344,7 +354,8 @@ // nodes for which we defer processing until we update the CFG. if (!isa(NewInst)) { RemapInstruction(NewInst, VMap, - ModuleLevelChanges ? RF_None : RF_NoModuleLevelChanges); + ModuleLevelChanges ? RF_None : RF_NoModuleLevelChanges, + TypeMapper, Materializer); // If we can simplify this instruction to some other value, simply add // a mapping to that value rather than inserting a new instruction into @@ -459,6 +470,14 @@ CloningDirector *Director) { assert(NameSuffix && "NameSuffix cannot be null!"); + ValueMapTypeRemapper *TypeMapper = nullptr; + ValueMaterializer *Materializer = nullptr; + + if (Director) { + TypeMapper = Director->getTypeRemapper(); + Materializer = Director->getValueMaterializer(); + } + #ifndef NDEBUG // If the cloning starts at the begining of the function, verify that // the function arguments are mapped. @@ -513,7 +532,8 @@ // Finally, remap the terminator instructions, as those can't be remapped // until all BBs are mapped. RemapInstruction(NewBB->getTerminator(), VMap, - ModuleLevelChanges ? RF_None : RF_NoModuleLevelChanges); + ModuleLevelChanges ? RF_None : RF_NoModuleLevelChanges, + TypeMapper, Materializer); } // Defer PHI resolution until rest of function is resolved, PHI resolution Index: test/CodeGen/X86/cppeh-catch-all.ll =================================================================== --- test/CodeGen/X86/cppeh-catch-all.ll +++ test/CodeGen/X86/cppeh-catch-all.ll @@ -55,8 +55,8 @@ ; CHECK: define i8* @_Z4testv.catch(i8*, i8*) { ; CHECK: catch.entry: ; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) -; CHECK: %ehdata = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* -; CHECK: %eh.obj.ptr = getelementptr inbounds %struct._Z4testv.ehdata* %ehdata, i32 0, i32 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: call void @_Z16handle_exceptionv() ; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont) Index: test/CodeGen/X86/cppeh-catch-scalar.ll =================================================================== --- test/CodeGen/X86/cppeh-catch-scalar.ll +++ test/CodeGen/X86/cppeh-catch-scalar.ll @@ -6,8 +6,8 @@ ; { ; try { ; may_throw(); -; } catch (int) { -; handle_int(); +; } catch (int i) { +; handle_int(i); ; } ; } ; @@ -18,13 +18,27 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 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 } + @_ZTIi = external constant i8* +; The function entry will be rewritten like this. +; CHECK: define void @_Z4testv() #0 { +; CHECKL entry: +; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 24) +; 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 +; CHECT: %i = getelementptr inbounds %struct._Z4testv.ehdata* %eh.data, i32 0, i32 2 + ; Function Attrs: uwtable define void @_Z4testv() #0 { entry: %exn.slot = alloca i8* %ehselector.slot = alloca i32 + %i = alloca i32, align 4 invoke void @_Z9may_throwv() to label %invoke.cont unwind label %lpad @@ -50,7 +64,10 @@ %exn11 = load i8** %exn.slot %4 = call i8* @llvm.eh.begincatch(i8* %exn11) #3 %5 = bitcast i8* %4 to i32* - call void @_Z10handle_intv() + %6 = load i32* %5, align 4 + store i32 %6, i32* %i, align 4 + %7 = load i32* %i, align 4 + call void @_Z10handle_inti(i32 %7) br label %invoke.cont2 invoke.cont2: ; preds = %catch @@ -71,11 +88,15 @@ ; CHECK: define i8* @_Z4testv.catch(i8*, i8*) { ; CHECK: catch.entry: ; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1) -; CHECK: %ehdata = bitcast i8* %eh.alloc to %struct._Z4testv.ehdata* -; CHECK: %eh.obj.ptr = getelementptr inbounds %struct._Z4testv.ehdata* %ehdata, i32 0, i32 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: %2 = bitcast i8* %eh.obj to i32* -; CHECK: call void @_Z10handle_intv() +; CHECK: %3 = load i32* %2, align 4 +; CHECK: store i32 %3, i32* %i, align 4 +; CHECK: %4 = load i32* %i, align 4 +; CHECK: call void @_Z10handle_inti(i32 %4) ; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont) ; CHECK: } @@ -90,7 +111,7 @@ declare void @llvm.eh.endcatch() -declare void @_Z10handle_intv() #1 +declare void @_Z10handle_inti(i32) #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" } Index: test/CodeGen/X86/cppeh-frame-vars.ll =================================================================== --- test/CodeGen/X86/cppeh-frame-vars.ll +++ test/CodeGen/X86/cppeh-frame-vars.ll @@ -0,0 +1,261 @@ +; 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); +; } + +; 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 } + +; 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 } + +$"\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 + +; 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: %NumExceptions = alloca i32, align 4 +; CHECK: %NumExceptions = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 +; 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-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: %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 + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %NumExceptions = alloca i32, align 4 + %ExceptionVal = alloca [10 x i32], align 16 + %Data = alloca %struct.SomeData, align 4 + %i = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %e = alloca i32, align 4 + store i32 0, i32* %NumExceptions, align 4 + %0 = bitcast %struct.SomeData* %Data to i8* + call void @llvm.memset(i8* %0, i8 0, i64 8, i32 4, i1 false) + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %1 = load i32* %i, align 4 + %cmp = icmp slt i32 %1, 10 + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %for.body + %2 = load i32* %i, align 4 + %a = getelementptr inbounds %struct.SomeData* %Data, i32 0, i32 0 + %3 = load i32* %a, align 4 + %add = add nsw i32 %3, %2 + store i32 %add, i32* %a, align 4 + br label %try.cont + +lpad: ; preds = %for.body + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %5 = extractvalue { i8*, i32 } %4, 0 + store i8* %5, i8** %exn.slot + %6 = extractvalue { i8*, i32 } %4, 1 + store i32 %6, i32* %ehselector.slot + br label %catch.dispatch + +catch.dispatch: ; preds = %lpad + %sel = load i32* %ehselector.slot + %7 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #1 + %matches = icmp eq i32 %sel, %7 + br i1 %matches, label %catch, label %eh.resume + +catch: ; preds = %catch.dispatch + %exn = load i8** %exn.slot + %8 = call i8* @llvm.eh.begincatch(i8* %exn) #1 + %9 = bitcast i8* %8 to i32* + %10 = load i32* %9, align 4 + store i32 %10, i32* %e, align 4 + %11 = load i32* %e, align 4 + %12 = load i32* %NumExceptions, align 4 + %idxprom = sext i32 %12 to i64 + %arrayidx = getelementptr inbounds [10 x i32]* %ExceptionVal, i32 0, i64 %idxprom + store i32 %11, i32* %arrayidx, align 4 + %13 = load i32* %NumExceptions, align 4 + %inc = add nsw i32 %13, 1 + store i32 %inc, i32* %NumExceptions, align 4 + %14 = load i32* %e, align 4 + %15 = load i32* %i, align 4 + %cmp1 = icmp eq i32 %14, %15 + br i1 %cmp1, label %if.then, label %if.else + +if.then: ; preds = %catch + %16 = load i32* %e, align 4 + %b = getelementptr inbounds %struct.SomeData* %Data, i32 0, i32 1 + %17 = load i32* %b, align 4 + %add2 = add nsw i32 %17, %16 + store i32 %add2, i32* %b, align 4 + br label %if.end + +if.else: ; preds = %catch + %18 = load i32* %e, align 4 + %a3 = getelementptr inbounds %struct.SomeData* %Data, i32 0, i32 0 + %19 = load i32* %a3, align 4 + %add4 = add nsw i32 %19, %18 + store i32 %add4, i32* %a3, align 4 + br label %if.end + +if.end: ; preds = %if.else, %if.then + call void @llvm.eh.endcatch() #1 + br label %try.cont + +try.cont: ; preds = %if.end, %invoke.cont + %20 = load i32* %NumExceptions, align 4 + call void @"\01?does_not_throw@@YAXH@Z"(i32 %20) + br label %for.inc + +for.inc: ; preds = %try.cont + %21 = load i32* %i, align 4 + %inc5 = add nsw i32 %21, 1 + store i32 %inc5, i32* %i, align 4 + br label %for.cond + +for.end: ; preds = %for.cond + %22 = load i32* %NumExceptions, align 4 + %arraydecay = getelementptr inbounds [10 x i32]* %ExceptionVal, i32 0, i32 0 + call void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32* %arraydecay, i32 %22, %struct.SomeData* dereferenceable(8) %Data) + ret void + +eh.resume: ; preds = %catch.dispatch + %exn6 = load i8** %exn.slot + %sel7 = load i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn6, 0 + %lpad.val8 = insertvalue { i8*, i32 } %lpad.val, i32 %sel7, 1 + resume { i8*, i32 } %lpad.val8 +} + +; The following catch handler should be outlined. +; CHECK: define i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) { +; CHECK: catch.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: %2 = bitcast i8* %eh.obj to i32* +; CHECK: %3 = load i32* %2, align 4 +; CHECK: store i32 %3, i32* %e, align 4 +; CHECK: %4 = load i32* %e, align 4 +; CHECK: %5 = load i32* %NumExceptions, align 4 +; CHECK: %idxprom = sext i32 %5 to i64 +; CHECK: %arrayidx = getelementptr inbounds [10 x i32]* %ExceptionVal, i32 0, i64 %idxprom +; CHECK: store i32 %4, i32* %arrayidx, align 4 +; CHECK: %6 = load i32* %NumExceptions, align 4 +; CHECK: %inc = add nsw i32 %6, 1 +; CHECK: store i32 %inc, i32* %NumExceptions, align 4 +; CHECK: %7 = load i32* %e, align 4 +; CHECK: %8 = load i32* %i, align 4 +; CHECK: %cmp1 = icmp eq i32 %7, %8 +; CHECK: br i1 %cmp1, label %if.then, label %if.else +; +; CHECK: if.then: ; preds = %catch.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 +; CHECK: %add2 = add nsw i32 %10, %9 +; CHECK: store i32 %add2, i32* %b, align 4 +; CHECK: br label %if.end +; +; CHECK: if.else: ; preds = %catch.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 +; CHECK: %add4 = add nsw i32 %12, %11 +; CHECK: store i32 %add4, i32* %a3, align 4 +; 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.memset(i8* nocapture, i8, i64, i32, i1) #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 + +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)"}