Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ lib/CodeGen/WinEHPrepare.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/TinyPtrVector.h" +#include "llvm/ADT/SetVector.h" #include "llvm/Analysis/LibCallSemantics.h" #include "llvm/CodeGen/WinEHFuncInfo.h" #include "llvm/IR/Dominators.h" @@ -86,6 +87,8 @@ bool prepareExceptionHandlers(Function &F, SmallVectorImpl &LPads); void promoteLandingPadValues(LandingPadInst *LPad); + void demoteValuesLiveAcrossHandlers(Function &F, + SmallVectorImpl &LPads); void completeNestedLandingPad(Function *ParentFn, LandingPadInst *OutlinedLPad, const LandingPadInst *OriginalLPad, @@ -319,6 +322,10 @@ cl::desc("Prepare functions with SEH personalities")); bool WinEHPrepare::runOnFunction(Function &Fn) { + // No need to prepare outlined handlers. + if (Fn.hasFnAttribute("wineh-parent")) + return false; + SmallVector LPads; SmallVector Resumes; for (BasicBlock &BB : Fn) { @@ -362,8 +369,164 @@ AU.addRequired(); } +// Finds blocks reachable from the starting set Worklist. Does not follow unwind +// edges or cross llvm.eh.endcatch calls. +static void findReachableBlocks(SmallPtrSetImpl &ReachableBBs, + SetVector &Worklist) { + while (!Worklist.empty()) { + BasicBlock *BB = Worklist.pop_back_val(); + if (!ReachableBBs.insert(BB).second) + continue; // Already visited. + + // Don't cross llvm.eh.endcatch. + auto *Br = dyn_cast(BB->getTerminator()); + if (Br && Br->getPrevNode() && + match(Br->getPrevNode(), m_Intrinsic())) + continue; + + // Don't follow unwind edges of invokes. + if (InvokeInst *II = dyn_cast(BB->getTerminator())) { + Worklist.insert(II->getNormalDest()); + continue; + } + + // Otherwise, follow all successors. + Worklist.insert(succ_begin(BB), succ_end(BB)); + } +} + +/// Ensure that all values live into and out of exception handlers are stored +/// in memory. +/// FIXME: This falls down when values are defined in one handler and live into +/// another handler. For example, a cleanup defines a value used only by a +/// catch handler. +void WinEHPrepare::demoteValuesLiveAcrossHandlers( + Function &F, SmallVectorImpl &LPads) { + // Build a set of all non-exceptional blocks and exceptional blocks. + // - Non-exceptional blocks are blocks reachable from the entry block while + // not following invoke unwind edges. + // - Exceptional blocks are blocks reachable from landingpads. Analysis does + // not follow llvm.eh.endcatch blocks, which mark a transition from + // exceptional to normal control. + SmallPtrSet NormalBlocks; + SmallPtrSet EHBlocks; + SetVector Worklist; + + Worklist.insert(&F.getEntryBlock()); + // Find all points where exceptional control rejoins normal control flow via + // llvm.eh.endcatch. Add them to the normal bb reachability worklist. + SmallVector BBs; + for (auto BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) { + BasicBlock *BB = BBI; + for (Instruction &I : *BB) { + if (match(&I, m_Intrinsic())) { + auto *Br = dyn_cast(I.getNextNode()); + if (!Br || !Br->isUnconditional()) + BBI = BB->splitBasicBlock(&I); + // The next BB is normal control flow. + Worklist.insert(BB->getTerminator()->getSuccessor(0)); + break; + } + } + } + findReachableBlocks(NormalBlocks, Worklist); + + // Add all landingpads to the exceptional control worklist. + Worklist.clear(); + for (auto *LPI : LPads) + Worklist.insert(LPI->getParent()); + findReachableBlocks(EHBlocks, Worklist); + + DEBUG(dbgs() << "Demoting values live across exception handlers in function " + << F.getName() << '\n'); + + SetVector ArgsToDemote; + SetVector InstrsToDemote; + for (BasicBlock &BB : F) { + bool IsNormalBB = NormalBlocks.count(&BB); + bool IsEHBB = EHBlocks.count(&BB); + if (!IsNormalBB && !IsEHBB) + continue; // Blocks that are neither normal nor EH are unreachable. + for (Instruction &I : BB) { + for (Value *Op : I.operands()) { + // Don't demote static allocas, constants, and labels. + if (isa(Op) || isa(Op)) + continue; + auto *AI = dyn_cast(Op); + if (AI && AI->isStaticAlloca()) + continue; + + if (auto *Arg = dyn_cast(Op)) { + if (IsEHBB) { + DEBUG(dbgs() << "Demoting argument " << *Arg + << " used by EH instr: " << I << "\n"); + ArgsToDemote.insert(Arg); + } + continue; + } + + auto *OpI = cast(Op); + BasicBlock *OpBB = OpI->getParent(); + // If a value is produced and consumed in the same BB, we don't need to + // demote it. + if (OpBB == &BB) + continue; + bool IsOpNormalBB = NormalBlocks.count(OpBB); + bool IsOpEHBB = EHBlocks.count(OpBB); + if (IsNormalBB != IsOpNormalBB || IsEHBB != IsOpEHBB) { + DEBUG({ + dbgs() << "Demoting instruction live in-out from EH:\n"; + dbgs() << "Instr: " << *OpI << '\n'; + dbgs() << "User: " << I << '\n'; + }); + InstrsToDemote.insert(OpI); + } + } + } + } + + // Demote values live into and out of handlers. + // FIXME: This demotion is inefficient. We should insert spills at the point + // of definition, insert one reload in each handler that uses the value, and + // insert reloads in the BB used to rejoin normal control flow. + Instruction *AllocaInsertPt = F.getEntryBlock().getFirstInsertionPt(); + for (Instruction *I : InstrsToDemote) + DemoteRegToStack(*I, false, AllocaInsertPt); + + // Demote arguments separately, and only for uses in EH blocks. + for (Argument *Arg : ArgsToDemote) { + auto *Slot = new AllocaInst(Arg->getType(), nullptr, + Arg->getName() + ".reg2mem", AllocaInsertPt); + SmallVector Users(Arg->user_begin(), Arg->user_end()); + for (User *U : Users) { + auto *I = dyn_cast(U); + if (I && EHBlocks.count(I->getParent())) { + auto *Reload = new LoadInst(Slot, Arg->getName() + ".reload", false, I); + U->replaceUsesOfWith(Arg, Reload); + } + } + new StoreInst(Arg, Slot, AllocaInsertPt); + } + + DEBUG(dbgs() << "Demoted " << InstrsToDemote.size() << " instructions and " + << ArgsToDemote.size() << " arguments for WinEHPrepare\n\n"); +} + bool WinEHPrepare::prepareExceptionHandlers( Function &F, SmallVectorImpl &LPads) { + // Don't run on functions that are already prepared. + for (LandingPadInst *LPad : LPads) { + BasicBlock *LPadBB = LPad->getParent(); + for (Instruction &Inst : *LPadBB) { + if (auto *IntrinCall = dyn_cast(&Inst)) { + if (IntrinCall->getIntrinsicID() == Intrinsic::eh_actions) + return false; + } + } + } + + demoteValuesLiveAcrossHandlers(F, LPads); + // These containers are used to re-map frame variables that are used in // outlined catch and cleanup handlers. They will be populated as the // handlers are outlined. @@ -486,14 +649,17 @@ CallInst::Create(ActionIntrin, ActionArgs, "recover", NewLPadBB); // Add an indirect branch listing possible successors of the catch handlers. - IndirectBrInst *Branch = IndirectBrInst::Create(Recover, 0, NewLPadBB); + SetVector ReturnTargets; for (ActionHandler *Action : Actions) { if (auto *CatchAction = dyn_cast(Action)) { - for (auto *Target : CatchAction->getReturnTargets()) { - Branch->addDestination(Target); - } + const auto &CatchTargets = CatchAction->getReturnTargets(); + ReturnTargets.insert(CatchTargets.begin(), CatchTargets.end()); } } + IndirectBrInst *Branch = + IndirectBrInst::Create(Recover, ReturnTargets.size(), NewLPadBB); + for (BasicBlock *Target : ReturnTargets) + Branch->addDestination(Target); } // End for each landingpad // If nothing got outlined, there is no more processing to be done. @@ -523,52 +689,11 @@ // Finally, replace all of the temporary allocas for frame variables used in // the outlined handlers with calls to llvm.framerecover. - BasicBlock::iterator II = Entry->getFirstInsertionPt(); - Instruction *AllocaInsertPt = II; SmallVector AllocasToEscape; for (auto &VarInfoEntry : FrameVarInfo) { Value *ParentVal = VarInfoEntry.first; TinyPtrVector &Allocas = VarInfoEntry.second; - - // If the mapped value isn't already an alloca, we need to spill it if it - // is a computed value or copy it if it is an argument. - AllocaInst *ParentAlloca = dyn_cast(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 = cast(ParentVal); - // FIXME: This is a work-around to temporarily handle the case where an - // instruction that is only used in handlers is not sunk. - // Without uses, DemoteRegToStack would just eliminate the value. - // This will fail if ParentInst is an invoke. - if (ParentInst->getNumUses() == 0) { - BasicBlock::iterator InsertPt = ParentInst; - ++InsertPt; - ParentAlloca = - new AllocaInst(ParentInst->getType(), nullptr, - ParentInst->getName() + ".reg2mem", - AllocaInsertPt); - new StoreInst(ParentInst, ParentAlloca, InsertPt); - } else { - ParentAlloca = DemoteRegToStack(*ParentInst, true, AllocaInsertPt); - } - } - } + AllocaInst *ParentAlloca = cast(ParentVal); // FIXME: We should try to sink unescaped allocas from the parent frame into // the child frame. If the alloca is escaped, we have to use the lifetime @@ -1326,31 +1451,23 @@ } Value *WinEHFrameVariableMaterializer::materializeValueFor(Value *V) { - // If we're asked to materialize a value that is an instruction, we - // temporarily create an alloca in the outlined function and add this - // to the FrameVarInfo map. When all the outlining is complete, we'll - // collect these into a structure, spilling non-alloca values in the - // parent frame as necessary, and replace these temporary allocas with - // GEPs referencing the frame allocation block. - - // If the value is an alloca, the mapping is direct. + // If we're asked to materialize a static alloca, we temporarily create an + // alloca in the outlined function and add this to the FrameVarInfo map. When + // all the outlining is complete, we'll replace these temporary allocas with + // calls to llvm.framerecover. if (auto *AV = dyn_cast(V)) { + assert(AV->isStaticAlloca() && + "cannot materialize un-demoted dynamic alloca"); AllocaInst *NewAlloca = dyn_cast(AV->clone()); Builder.Insert(NewAlloca, AV->getName()); FrameVarInfo[AV].push_back(NewAlloca); return NewAlloca; } - // For other types of instructions or arguments, we need an alloca based on - // the value's type and a load of the alloca. The alloca will be replaced - // by a GEP, but the load will stay. In the parent function, the value will - // be spilled to a location in the frame allocation block. if (isa(V) || isa(V)) { - AllocaInst *NewAlloca = - Builder.CreateAlloca(V->getType(), nullptr, "eh.temp.alloca"); - FrameVarInfo[V].push_back(NewAlloca); - LoadInst *NewLoad = Builder.CreateLoad(NewAlloca, V->getName() + ".reload"); - return NewLoad; + dbgs() << "Failed to demote instruction used in exception handler:\n"; + dbgs() << " " << *V << '\n'; + report_fatal_error("WinEHPrepare failed to demote instruction"); } // Don't materialize other values. Index: test/CodeGen/WinEH/cppeh-inalloca.ll =================================================================== --- test/CodeGen/WinEH/cppeh-inalloca.ll +++ test/CodeGen/WinEH/cppeh-inalloca.ll @@ -39,8 +39,7 @@ ; CHECK: define i32 @"\01?test@@YAHUA@@@Z"(<{ %struct.A }>* inalloca) ; CHECK: entry: ; CHECK: [[TMP_REGMEM:\%.+]] = alloca <{ %struct.A }>* -; CHECK: [[TMP:\%.+]] = select i1 true, <{ %struct.A }>* %0, <{ %struct.A }>* undef -; CHECK: store <{ %struct.A }>* [[TMP]], <{ %struct.A }>** [[TMP_REGMEM]] +; CHECK: store <{ %struct.A }>* %0, <{ %struct.A }>** [[TMP_REGMEM]] ; CHECK: [[RETVAL:\%.+]] = alloca i32, align 4 ; CHECK: [[E_PTR:\%.+]] = alloca i32, align 4 ; CHECK: [[CLEANUP_SLOT:\%.+]] = alloca i32 @@ -109,10 +108,8 @@ ; The cleanup block should be re-written like this. ; CHECK: cleanup:{{[ ]+}}; preds = %[[LPAD_LABEL]], %try.cont -; CHECK-NOT: %a2 = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* %0, i32 0, i32 0 -; CHECK: [[TMP_RELOAD:\%.+]] = load volatile <{ %struct.A }>*, <{ %struct.A }>** [[TMP_REGMEM]] -; CHECK: [[A2:\%.+]] = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* [[TMP_RELOAD]], i32 0, i32 0 -; CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* [[A2]]) +; CHECK: %a2 = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* %0, i32 0, i32 0 +; CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %a2) ; CHECK: [[TMP1:\%.+]] = load i32, i32* [[RETVAL]] ; CHECK: ret i32 [[TMP1]] @@ -148,12 +145,12 @@ ; CHECK: [[E_PTR:\%.+]] = bitcast i8* [[RECOVER_E]] to i32* ; CHECK: [[RECOVER_EH_TEMP:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 1) ; CHECK: [[EH_TEMP:\%.+]] = bitcast i8* [[RECOVER_EH_TEMP]] to <{ %struct.A }>** -; CHECK: [[TMP_RELOAD:\%.+]] = load <{ %struct.A }>*, <{ %struct.A }>** [[EH_TEMP]] ; CHECK: [[RECOVER_RETVAL:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 2) ; CHECK: [[RETVAL1:\%.+]] = bitcast i8* [[RECOVER_RETVAL]] to i32* ; CHECK: [[RECOVER_CLEANUPSLOT:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 3) ; CHECK: [[CLEANUPSLOT1:\%.+]] = bitcast i8* [[RECOVER_CLEANUPSLOT]] to i32* ; CHECK: [[E_I8PTR:\%.+]] = bitcast i32* [[E_PTR]] to i8* +; CHECK: [[TMP_RELOAD:\%.+]] = load <{ %struct.A }>*, <{ %struct.A }>** [[EH_TEMP]] ; CHECK: [[RECOVER_A:\%.+]] = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* [[TMP_RELOAD]], i32 0, i32 0 ; CHECK: [[A1:\%.+]] = getelementptr inbounds %struct.A, %struct.A* [[RECOVER_A]], i32 0, i32 0 ; CHECK: [[TMP2:\%.+]] = load i32, i32* [[A1]], align 4 Index: test/CodeGen/WinEH/cppeh-multi-catch.ll =================================================================== --- test/CodeGen/WinEH/cppeh-multi-catch.ll +++ test/CodeGen/WinEH/cppeh-multi-catch.ll @@ -79,7 +79,7 @@ ; CHECK-SAME: i32 1, i8* bitcast (%eh.HandlerMapEntry* @llvm.eh.handlermapentry._J to i8*), i32 1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch1", ; CHECK-SAME: i32 1, i8* bitcast (%eh.HandlerMapEntry* @"llvm.eh.handlermapentry.reference.?AVSomeClass@@" to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch2", ; CHECK-SAME: i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch3") -; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %catch14.split, label %catch10.split, label %catch6.split, label %catch.split] +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %ret] lpad: ; preds = %entry %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) @@ -100,8 +100,11 @@ %matches = icmp eq i32 %sel, %3 br i1 %matches, label %catch14, label %catch.fallthrough +ret: + ret void + ; CHECK-NOT: catch14: -; CHECK: catch14.split: +; CHECK: ret: ; CHECK-NEXT: ret void catch14: ; preds = %catch.dispatch %exn15 = load i8*, i8** %exn.slot @@ -110,10 +113,10 @@ %5 = load i32, i32* %i, align 4 call void @"\01?handle_int@@YAXH@Z"(i32 %5) call void @llvm.eh.endcatch() #3 - ret void + br label %ret try.cont: ; preds = %invoke.cont - ret void + br label %ret ; CHECK-NOT: catch.fallthrough: catch.fallthrough: ; preds = %catch.dispatch @@ -122,8 +125,6 @@ br i1 %matches1, label %catch10, label %catch.fallthrough2 ; CHECK-NOT: catch10: -; CHECK: catch10.split: -; CHECK-NEXT: ret void catch10: ; preds = %catch.fallthrough %exn11 = load i8*, i8** %exn.slot %7 = bitcast i64* %ll to i8* @@ -131,7 +132,7 @@ %8 = load i64, i64* %ll, align 8 call void @"\01?handle_long_long@@YAX_J@Z"(i64 %8) call void @llvm.eh.endcatch() #3 - ret void + br label %ret ; CHECK-NOT: catch.fallthrough2: catch.fallthrough2: ; preds = %catch.fallthrough @@ -140,8 +141,6 @@ br i1 %matches3, label %catch6, label %catch ; CHECK-NOT: catch6: -; CHECK: catch6.split: -; CHECK-NEXT: ret void catch6: ; preds = %catch.fallthrough2 %exn7 = load i8*, i8** %exn.slot %10 = bitcast %class.SomeClass** %obj to i8* @@ -149,16 +148,14 @@ %11 = load %class.SomeClass*, %class.SomeClass** %obj, align 8 call void @"\01?handle_obj@@YAXPEAVSomeClass@@@Z"(%class.SomeClass* %11) call void @llvm.eh.endcatch() #3 - ret void + br label %ret ; CHECK-NOT: catch: -; CHECK: catch.split: -; CHECK-NEXT: ret void catch: ; preds = %catch.fallthrough2 %exn = load i8*, i8** %exn.slot call void @llvm.eh.begincatch(i8* %exn, i8* null) #3 call void @"\01?handle_exception@@YAXXZ"() call void @llvm.eh.endcatch() #3 - ret void + br label %ret ; CHECK: } } @@ -168,7 +165,7 @@ ; CHECK: [[I_PTR:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* ; CHECK: [[TMP1:\%.+]] = load i32, i32* [[I_PTR]], align 4 ; CHECK: call void @"\01?handle_int@@YAXH@Z"(i32 [[TMP1]]) -; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %catch14.split) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %ret) ; CHECK: } ; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch1"(i8*, i8*) @@ -177,7 +174,7 @@ ; CHECK: [[LL_PTR:\%.+]] = bitcast i8* [[RECOVER_LL]] to i64* ; CHECK: [[TMP2:\%.+]] = load i64, i64* [[LL_PTR]], align 8 ; CHECK: call void @"\01?handle_long_long@@YAX_J@Z"(i64 [[TMP2]]) -; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %catch10.split) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %ret) ; CHECK: } ; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch2"(i8*, i8*) @@ -186,13 +183,13 @@ ; CHECK: [[OBJ_PTR:\%.+]] = bitcast i8* [[RECOVER_OBJ]] to %class.SomeClass** ; CHECK: [[TMP3:\%.+]] = load %class.SomeClass*, %class.SomeClass** [[OBJ_PTR]], align 8 ; CHECK: call void @"\01?handle_obj@@YAXPEAVSomeClass@@@Z"(%class.SomeClass* [[TMP3]]) -; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %catch6.split) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %ret) ; CHECK: } ; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch3"(i8*, i8*) ; CHECK: entry: ; CHECK: call void @"\01?handle_exception@@YAXXZ"() -; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %catch.split) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %ret) ; CHECK: } Index: test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll =================================================================== --- test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll +++ test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll @@ -55,8 +55,8 @@ ; CHECK: entry: ; CHECK: [[NUMEXCEPTIONS_REGMEM:\%.+]] = alloca i32 ; CHECK: [[I_REGMEM:\%.+]] = alloca i32 -; CHECK: [[A_REGMEM:\%.+]] = alloca i32* ; CHECK: [[B_REGMEM:\%.+]] = alloca i32* +; CHECK: [[A_REGMEM:\%.+]] = alloca i32* ; CHECK: [[E_PTR:\%.+]] = alloca i32, align 4 ; CHECK: [[EXCEPTIONVAL:\%.+]] = alloca [10 x i32], align 16 ; CHECK: [[DATA_PTR:\%.+]] = alloca i64, align 8 @@ -68,9 +68,7 @@ ; CHECK: store i32* [[A_PTR]], i32** [[A_REGMEM]] ; CHECK: [[B_PTR:\%.+]] = getelementptr inbounds %struct.SomeData, %struct.SomeData* [[TMPCAST]], i64 0, i32 1 ; CHECK: store i32* [[B_PTR]], i32** [[B_REGMEM]] -; CHECK: store i32 0, i32* [[NUMEXCEPTIONS_REGMEM]] -; CHECK: store i32 0, i32* [[I_REGMEM]] -; CHECK: call void (...) @llvm.frameescape(i32* %e, i32* %NumExceptions.020.reg2mem, [10 x i32]* [[EXCEPTIONVAL]], i32* [[I_REGMEM]], i32** [[A_REGMEM]], i32** [[B_REGMEM]]) +; CHECK: call void (...) @llvm.frameescape(i32* %e, i32* %NumExceptions.020.reg2mem, [10 x i32]* [[EXCEPTIONVAL]], i32* %inc.reg2mem, i32* [[I_REGMEM]], i32** [[A_REGMEM]], i32** [[B_REGMEM]]) ; CHECK: br label %for.body ; Function Attrs: uwtable @@ -88,10 +86,11 @@ br label %for.body ; CHECK: for.body: -; CHECK-NOT: phi i32 [ 0, %entry ], [ {{\%NumExceptions.*}}, %try.cont ] -; CHECK-NOT: phi i32 [ 0, %entry ], [ {{\%inc.*}}, %try.cont ] -; CHECK: [[I_RELOAD:\%.+]] = load i32, i32* [[I_REGMEM]] -; CHECK: [[NUMEXCEPTIONS_RELOAD:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_REGMEM]] +; CHECK: [[NUMEXCEPTIONS_PHI:\%.*]] = phi i32 [ 0, %entry ], [ {{\%NumExceptions.*}}, %try.cont ] +; CHECK: [[I_PHI:\%.*]] = phi i32 [ 0, %entry ], [ {{\%inc.*}}, %try.cont ] +; CHECK: store i32 [[I_PHI]], i32* [[I_REGMEM]] +; CHECK: store i32 [[NUMEXCEPTIONS_PHI]], i32* [[NUMEXCEPTIONS_REGMEM]] +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() 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 ] @@ -99,11 +98,12 @@ to label %invoke.cont unwind label %lpad ; CHECK: invoke.cont: ; preds = %for.body -; CHECK: [[A_RELOAD:\%.+]] = load volatile i32*, i32** [[A_REGMEM]] +; CHECK: [[A_RELOAD:\%.+]] = load i32*, i32** [[A_REGMEM]] ; CHECK: [[TMP1:\%.+]] = load i32, i32* [[A_RELOAD]], align 8 +; CHECK: [[I_RELOAD:\%.+]] = load i32, i32* [[I_REGMEM]] ; CHECK: [[ADD:\%.+]] = add nsw i32 [[TMP1]], [[I_RELOAD]] -; CHECK: [[A_RELOAD1:\%.+]] = load volatile i32*, i32** [[A_REGMEM]] -; CHECK: store i32 [[ADD]], i32* [[A_RELOAD1]], align 8 +; CHECK: [[A_RELOAD1:\%.+]] = load i32*, i32** [[A_REGMEM]] +; CHECK: [[NUMEXCEPTIONS_RELOAD:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_REGMEM]] ; CHECK: br label %try.cont invoke.cont: ; preds = %for.body %1 = load i32, i32* %a, align 8, !tbaa !2 @@ -115,7 +115,7 @@ ; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) ; CHECK-NEXT: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) ; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch") -; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %try.cont] +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %if.end.split] lpad: ; preds = %for.body %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) @@ -156,18 +156,20 @@ br label %if.end ; CHECK-NOT: if.end: +; CHECK: if.end.split: +; CHECK: [[INC_RELOAD:\%.*]] = load i32, i32* +; CHECK: br label %try.cont if.end: ; preds = %if.else, %if.then tail call void @llvm.eh.endcatch() #1 br label %try.cont -; CHECK: try.cont:{{[ ]+}}; preds = %[[LPAD_LABEL]], %invoke.cont -; CHECK-NOT: phi i32 -; CHECK: tail call void @"\01?does_not_throw@@YAXH@Z"(i32 [[NUMEXCEPTIONS_RELOAD]]) +; CHECK: try.cont:{{[ ]+}}; preds = %if.end.split, %invoke.cont +; CHECK: [[NUMEXCEPTIONS_PHI:\%.*]] = phi i32 [ [[NUMEXCEPTIONS_RELOAD]], %invoke.cont ], [ [[INC_RELOAD]], %if.end.split ] +; CHECK: tail call void @"\01?does_not_throw@@YAXH@Z"(i32 [[NUMEXCEPTIONS_PHI]]) +; CHECK: [[I_RELOAD:\%.+]] = load i32, i32* [[I_REGMEM]] ; CHECK: [[INC:\%.+]] = add nuw nsw i32 [[I_RELOAD]], 1 ; CHECK: [[CMP:\%.+]] = icmp slt i32 [[INC]], 10 -; CHECK: store i32 [[NUMEXCEPTIONS_RELOAD]], i32* [[NUMEXCEPTIONS_REGMEM]] -; CHECK: store i32 [[INC]], i32* [[I_REGMEM]] ; CHECK: br i1 [[CMP]], label %for.body, label %for.end try.cont: ; preds = %if.end, %invoke.cont @@ -194,43 +196,48 @@ ; CHECK: entry: ; CHECK: [[RECOVER_E:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) ; CHECK: [[E_PTR:\%.+]] = bitcast i8* [[RECOVER_E]] to i32* -; CHECK: [[RECOVER_EH_TEMP:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) -; CHECK: [[EH_TEMP:\%.+]] = bitcast i8* [[RECOVER_EH_TEMP]] to i32* -; CHECK: [[NUMEXCEPTIONS_RELOAD:\%.+]] = load i32, i32* [[EH_TEMP]] +; CHECK: [[RECOVER_NUMEXCEPTIONS:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: [[NUMEXCEPTIONS_REGMEM:\%.+]] = bitcast i8* [[RECOVER_NUMEXCEPTIONS]] to i32* ; CHECK: [[RECOVER_EXCEPTIONVAL:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) ; CHECK: [[EXCEPTIONVAL:\%.+]] = bitcast i8* [[RECOVER_EXCEPTIONVAL]] to [10 x i32]* -; CHECK: [[RECOVER_EH_TEMP1:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 3) -; CHECK: [[EH_TEMP1:\%.+]] = bitcast i8* [[RECOVER_EH_TEMP1]] to i32* -; CHECK: [[I_RELOAD:\%.+]] = load i32, i32* [[EH_TEMP1]] -; CHECK: [[RECOVER_EH_TEMP2:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 4) -; CHECK: [[EH_TEMP2:\%.+]] = bitcast i8* [[RECOVER_EH_TEMP2]] to i32** -; CHECK: [[A_RELOAD:\%.+]] = load i32*, i32** [[EH_TEMP2]] -; CHECK: [[RECOVER_EH_TEMP3:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 5) -; CHECK: [[EH_TEMP3:\%.+]] = bitcast i8* [[RECOVER_EH_TEMP3]] to i32** -; CHECK: [[B_RELOAD:\%.+]] = load i32*, i32** [[EH_TEMP3]] +; CHECK: [[RECOVER_INC:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 3) +; CHECK: [[INC_REGMEM:\%.+]] = bitcast i8* [[RECOVER_INC]] to i32* +; CHECK: [[RECOVER_I:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 4) +; CHECK: [[I_REGMEM:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* +; CHECK: [[RECOVER_A:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 5) +; CHECK: [[A_REGMEM:\%.+]] = bitcast i8* [[RECOVER_A]] to i32** +; CHECK: [[RECOVER_B:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 6) +; CHECK: [[B_REGMEM:\%.+]] = bitcast i8* [[RECOVER_B]] to i32** ; CHECK: [[E_I8PTR:\%.+]] = bitcast i32* [[E_PTR]] to i8* ; CHECK: [[TMP:\%.+]] = load i32, i32* [[E_PTR]], align 4 +; CHECK: [[NUMEXCEPTIONS_RELOAD:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_REGMEM]] ; CHECK: [[IDXPROM:\%.+]] = sext i32 [[NUMEXCEPTIONS_RELOAD]] to i64 ; CHECK: [[ARRAYIDX:\%.+]] = getelementptr inbounds [10 x i32], [10 x i32]* [[EXCEPTIONVAL]], i64 0, i64 [[IDXPROM]] ; CHECK: store i32 [[TMP]], i32* [[ARRAYIDX]], align 4 +; CHECK: [[NUMEXCEPTIONS_RELOAD:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_REGMEM]] ; CHECK: [[INC:\%.+]] = add nsw i32 [[NUMEXCEPTIONS_RELOAD]], 1 ; CHECK: [[CMP:\%.+]] = icmp eq i32 [[TMP]], [[I_RELOAD]] ; CHECK: br i1 [[CMP]], label %if.then, label %if.else ; ; CHECK: if.then:{{[ ]+}}; preds = %entry +; CHECK: [[B_RELOAD:\%.+]] = load i32*, i32** [[B_REGMEM]] ; CHECK: [[TMP1:\%.+]] = load i32, i32* [[B_RELOAD]], align 4 +; CHECK: [[I_RELOAD:\%.+]] = load i32, i32* [[I_REGMEM]] ; CHECK: [[ADD:\%.+]] = add nsw i32 [[TMP1]], [[I_RELOAD]] +; CHECK: [[B_RELOAD:\%.+]] = load i32*, i32** [[B_REGMEM]] ; CHECK: store i32 [[ADD]], i32* [[B_RELOAD]], align 4 ; CHECK: br label %if.end ; ; CHECK: if.else:{{[ ]+}}; preds = %entry +; CHECK: [[A_RELOAD:\%.+]] = load i32*, i32** [[A_REGMEM]] ; CHECK: [[TMP2:\%.+]] = load i32, i32* [[A_RELOAD]], align 8 ; CHECK: [[ADD2:\%.+]] = add nsw i32 [[TMP2]], [[TMP]] +; CHECK: [[A_RELOAD:\%.+]] = load i32*, i32** [[A_REGMEM]] ; CHECK: store i32 [[ADD2]], i32* [[A_RELOAD]], align 8 ; CHECK: br label %if.end ; ; CHECK: if.end:{{[ ]+}}; preds = %if.else, %if.then -; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %if.end.split) ; CHECK: } ; Function Attrs: nounwind