Index: include/llvm/Transforms/Utils/Local.h =================================================================== --- include/llvm/Transforms/Utils/Local.h +++ include/llvm/Transforms/Utils/Local.h @@ -116,6 +116,10 @@ /// void MergeBasicBlockIntoOnlyPred(BasicBlock *BB, DominatorTree *DT = nullptr); +/// Replace an invoke instruction with a call of the same value followed by an +/// unconditional branch to the normal destination of the invoke. +void convertInvokeToCall(InvokeInst *II); + /// TryToSimplifyUncondBranchFromEmptyBlock - BB is known to contain an /// unconditional branch, and contains no instructions other than PHI nodes, /// potential debug intrinsics and the branch. If possible, eliminate BB by Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ lib/CodeGen/WinEHPrepare.cpp @@ -146,6 +146,13 @@ SmallVectorImpl &EntryBlocks); void replaceTerminatePadWithCleanup(Function &F); void colorFunclets(Function &F, SmallVectorImpl &EntryBlocks); + void resolveFuncletAncestry(Function &F); + void resolveFuncletAncestryForPath( + Function &F, SmallVectorImpl &FuncletPath, + std::map &IdentityMap); + void makeFuncletEdgeUnreachable(BasicBlock *Parent, BasicBlock *Child); + BasicBlock *cloneFuncletForParent(Function &F, BasicBlock *FuncletEntry, + BasicBlock *Parent); void demotePHIsOnFunclets(Function &F); void demoteUsesBetweenFunclets(Function &F); void demoteArgumentUses(Function &F); @@ -197,7 +204,8 @@ std::map> BlockColors; std::map> FuncletBlocks; - std::map> FuncletChildren; + std::map> FuncletChildren; + std::map> FuncletParents; }; class WinEHFrameVariableMaterializer : public ValueMaterializer { @@ -2968,13 +2976,347 @@ // that transitions to the child funclet). for (BasicBlock *FuncletEntry : EntryBlocks) { std::set &ColorMapItem = BlockColors[FuncletEntry]; - for (BasicBlock *Parent : ColorMapItem) - FuncletChildren[Parent].insert(FuncletEntry); + for (BasicBlock *Parent : ColorMapItem) { + assert(std::find(FuncletChildren[Parent].begin(), + FuncletChildren[Parent].end(), + FuncletEntry) == std::end(FuncletChildren[Parent])); + FuncletChildren[Parent].push_back(FuncletEntry); + assert(std::find(FuncletParents[FuncletEntry].begin(), + FuncletParents[FuncletEntry].end(), + Parent) == std::end(FuncletParents[FuncletEntry])); + FuncletParents[FuncletEntry].push_back(Parent); + } ColorMapItem.clear(); ColorMapItem.insert(FuncletEntry); } } +// Clones all blocks used by the specified funclet to avoid the funclet having +// multiple parent funclets. All terminators in the parent that unwind to the +// original funclet are remapped to unwind to the clone. Any terminator in the +// original funclet which returned to this parent is converted to an unreachable +// to an unreachable instruction. Likewise, any terminator in the cloned funclet +// which returns to a parent funclet other than the specified parent is +// converted to an unreachable instruction. +BasicBlock *WinEHPrepare::cloneFuncletForParent(Function &F, + BasicBlock *FuncletEntry, + BasicBlock *Parent) { + std::set &BlocksInFunclet = FuncletBlocks[FuncletEntry]; + + std::map Orig2Clone; + ValueToValueMapTy VMap; + for (BasicBlock *BB : BlocksInFunclet) { + // Create a new basic block and copy instructions into it. + BasicBlock *CBB = + CloneBasicBlock(BB, VMap, Twine(".from.", Parent->getName())); + + // Insert the clone immediately after the original to ensure determinism + // and to keep the same relative ordering of any funclet's blocks. + CBB->insertInto(&F, BB->getNextNode()); + + // Add basic block mapping. + VMap[BB] = CBB; + + // Record delta operations that we need to perform to our color mappings. + Orig2Clone[BB] = CBB; + + // Any path in the uncloned funclet that returns to the clone's parent + // must become unreachable. + TerminatorInst *Terminator = BB->getTerminator(); + if (auto *CRI = dyn_cast(Terminator)) { + // If we're cloning a catch pad and the uncloned catch pad returns to + // a block in the clone parent, we need to replace the catchret with + // an unreachable instruction. + if (FuncletBlocks[Parent].count(CRI->getSuccessor())) { + CRI->eraseFromParent(); + new UnreachableInst(BB->getContext(), BB); + // Loop over all of the successors, removing BB's entry from any PHI + // nodes. + for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE; + ++SI) + (*SI)->removePredecessor(BB); + } + } else if (auto *CRI = dyn_cast(Terminator)) { + // If we're cloning a cleanup pad and the uncloned cleanup pad unwinds + // to a block in the clone parent, we need to replace the cleanupret with + // an unreachable instruction. + BasicBlock *UnwindDest = CRI->getUnwindDest(); + if (UnwindDest && FuncletBlocks[Parent].count(UnwindDest)) { + CRI->eraseFromParent(); + new UnreachableInst(BB->getContext(), BB); + // Loop over all of the successors, removing BB's entry from any PHI + // nodes. + for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE; + ++SI) + (*SI)->removePredecessor(BB); + } + } + } + + BasicBlock *ClonedFunclet = Orig2Clone[FuncletEntry]; + assert(ClonedFunclet); + + // Set the coloring for the blocks we just cloned. + std::set &ClonedBlocks = FuncletBlocks[ClonedFunclet]; + for (auto &BBMapping : Orig2Clone) { + BasicBlock *NewBlock = BBMapping.second; + ClonedBlocks.insert(NewBlock); + BlockColors[NewBlock].insert(ClonedFunclet); + + // Use the VMap to remap the instructions in this cloned block. + for (Instruction &I : *NewBlock) + RemapInstruction(&I, VMap, RF_IgnoreMissingEntries); + + // Any unwind destination in the cloned funclet that isn't an expected + // funclet child or a block in the designated parent funclet must become + // unreachable. + TerminatorInst *Terminator = NewBlock->getTerminator(); + if (auto *CRI = dyn_cast(Terminator)) { + // If we're cloning a catch pad and the cloned catch pad returns to + // a block that is not in the clone parent, we need to replace the + // catchret with an unreachable instruction. + if (!FuncletBlocks[Parent].count(CRI->getSuccessor())) { + CRI->eraseFromParent(); + new UnreachableInst(NewBlock->getContext(), NewBlock); + // Loop over all of the successors, removing NewBlock's entry from any + // PHI nodes. + for (succ_iterator SI = succ_begin(NewBlock), SE = succ_end(NewBlock); + SI != SE; ++SI) + (*SI)->removePredecessor(NewBlock); + } + } else if (auto *CRI = dyn_cast(Terminator)) { + // If we're cloning a cleanup pad and the cloned cleanup pad does not + // unwind to a block in the clone parent, we need to replace the + // cleanupret with an unreachable instruction. + BasicBlock *UnwindDest = CRI->getUnwindDest(); + if (UnwindDest && !FuncletBlocks[Parent].count(UnwindDest)) { + CRI->eraseFromParent(); + new UnreachableInst(NewBlock->getContext(), NewBlock); + // Loop over all of the successors, removing NewBlock's entry from any + // PHI nodes. + for (succ_iterator SI = succ_begin(NewBlock), SE = succ_end(NewBlock); + SI != SE; ++SI) + (*SI)->removePredecessor(NewBlock); + } + } + + // Check to see if the cloned block successor has PHI nodes. If so, we need + // to add entries to the PHI nodes for the cloned block now. + BasicBlock *OldBlock = BBMapping.first; + for (BasicBlock *SuccBB : successors(NewBlock)) { + for (Instruction &SuccI : *SuccBB) { + auto *SuccPN = dyn_cast(&SuccI); + if (!SuccPN) + break; + + // Ok, we have a PHI node. Figure out what the incoming value was for + // the OldBlock. + int OldBlockIdx = SuccPN->getBasicBlockIndex(OldBlock); + if (OldBlockIdx == -1) + break; + Value *IV = SuccPN->getIncomingValue(OldBlockIdx); + + // Remap the value if necessary. + if (auto *Inst = dyn_cast(IV)) { + ValueToValueMapTy::iterator I = VMap.find(Inst); + if (I != VMap.end()) + IV = I->second; + } + + SuccPN->addIncoming(IV, NewBlock); + } + } + } + + + // Remove the clone's parent from the original funclet's parent list. + std::remove(FuncletParents[FuncletEntry].begin(), + FuncletParents[FuncletEntry].end(), Parent); + + // Store the cloned funclet's parent. + assert(std::find(FuncletParents[ClonedFunclet].begin(), + FuncletParents[ClonedFunclet].end(), + Parent) == std::end(FuncletParents[ClonedFunclet])); + FuncletParents[ClonedFunclet].push_back(Parent); + + // Copy any children of the original funclet to the clone. We'll either + // clone them too or make that path unreachable when we take the next step + // in resolveDuplicateFuncletAncestry(). + for (auto *Child : FuncletChildren[FuncletEntry]) { + assert(std::find(FuncletChildren[ClonedFunclet].begin(), + FuncletChildren[ClonedFunclet].end(), + Child) == std::end(FuncletChildren[ClonedFunclet])); + FuncletChildren[ClonedFunclet].push_back(Child); + assert(std::find(FuncletParents[Child].begin(), FuncletParents[Child].end(), + ClonedFunclet) == std::end(FuncletParents[Child])); + FuncletParents[Child].push_back(ClonedFunclet); + } + + // Find any place in the parent block that branched to the original funclet + // and remap it to the clone. + for (BasicBlock *BB : FuncletBlocks[Parent]) { + if (!FuncletEntry->isUsedInBasicBlock(BB)) + continue; + // We're about to break the path from this block to the uncloned funclet + // entry, so remove it as a predeccessor to clean up the PHIs. + FuncletEntry->removePredecessor(BB); + TerminatorInst *Terminator = BB->getTerminator(); + RemapInstruction(Terminator, VMap, RF_IgnoreMissingEntries); + } + + // This asserts a condition that is relied upon inside the loop below. + for (auto *Pred : predecessors(FuncletEntry)) + assert(std::find(pred_begin(ClonedFunclet), pred_end(ClonedFunclet), + Pred) == pred_end(ClonedFunclet)); + + // Remove any invalid PHI node entries in the cloned funclet. + std::vector PHIsToErase; + for (Instruction &I : *ClonedFunclet) { + auto *PN = dyn_cast(&I); + if (!PN) + break; + + // Predecessors of the original funclet do not reach the cloned funclet, + // but the cloning process assumes they will. Remove them now. + for (auto *Pred : predecessors(FuncletEntry)) + PN->removeIncomingValue(Pred, false); + + // Tidy up if the PHI node has become trivial. + unsigned RemainingValues = PN->getNumIncomingValues(); + if (!RemainingValues) { + PHIsToErase.push_back(PN); + } else if (RemainingValues == 1) { + auto *PV = PN->getIncomingValue(0); + if (PV != PN) + PN->replaceAllUsesWith(PV); + else + // We are left with an infinite loop with no entries: kill the PHI. + PN->replaceAllUsesWith(UndefValue::get(PN->getType())); + PHIsToErase.push_back(PN); + } + } + for (auto *PN : PHIsToErase) + PN->eraseFromParent(); + + return ClonedFunclet; +} + +// Modifies exceptional terminators within the specified parent funclet that +// previously unwound to the specified child funclet so that the unwind +// path is unreachable. In the case of invoke instructions which unwound to +// the child funclet, a cleanup pad with an unreachable instruction as its +// body is created and used as the unwind destination. +void WinEHPrepare::makeFuncletEdgeUnreachable(BasicBlock *Parent, + BasicBlock *Child) { + for (BasicBlock *BB : FuncletBlocks[Parent]) { + TerminatorInst *Terminator = BB->getTerminator(); + if (!Terminator->isExceptional()) + continue; + + // An invoke that unwinds to the child funclet must be handled as a + // special case. Simply converting the invoke to a call would leave + // the behavior in an ambiguous state. We want the unwind edge to + // actually be flagged as unreachable. + // + // The verifier requires the unwind edge of an invoke to be an exception + // block so we need to create one and populate it with an unreachable + // instruction. + if (auto *II = dyn_cast(Terminator)) { + BasicBlock *UnwindDest = II->getUnwindDest(); + if (UnwindDest == Child) { + Function *F = Parent->getParent(); + LLVMContext &Context = F->getContext(); + + // Create an unreachable block. + BasicBlock *UnreachableBB = BasicBlock::Create( + Context, Twine(Parent->getName(), ".unreachable"), F, UnwindDest); + CleanupPadInst::Create(Context, {}, "", UnreachableBB); + new UnreachableInst(Context, UnreachableBB); + II->setUnwindDest(UnreachableBB); + + // Set up the coloring for this new funclet. + BlockColors[UnreachableBB].insert(UnreachableBB); + // This doesn't undate FuncletBlocks, FuncletChildren, FuncletParents or + // EntryBlocks, but since the funclet is unreachable, that should be OK. + } + continue; + } + + // For other exceptional terminators, look for terninators that + // unwind to the child funclet. + BasicBlock *UnwindDest = nullptr; + if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + else if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + else if (auto *I = dyn_cast(Terminator)) + UnwindDest = I->getUnwindDest(); + // catchret and cleanupret don't represent a parent-to-child funclet + // transition, so we don't need to consider them here. + + // If the child funclet is the unwind destination, replace the terminator + // with an unreachable instruction. + if (UnwindDest == Child) { + Terminator->eraseFromParent(); + new UnreachableInst(BB->getContext(), BB); + } + } + // The specified parent is no longer a parent of the specified child. + std::remove(FuncletChildren[Parent].begin(), FuncletChildren[Parent].end(), + Child); +} + +void WinEHPrepare::resolveFuncletAncestry(Function &F) { + // Make sure each funclet has a unique parent. + std::map IdentityMap; + SmallVector FuncletPath; + FuncletPath.push_back(&(F.getEntryBlock())); + resolveFuncletAncestryForPath(F, FuncletPath, IdentityMap); +} + +// Walks the funclet control flow, cloning any funclets that have more than one +// parent funclet and breaking any cyclic unwind chains so that the path becomes +// unreachable at the point where a funclet would have unwound to a funclet that +// was already in the chain. +void WinEHPrepare::resolveFuncletAncestryForPath( + Function &F, SmallVectorImpl &FuncletPath, + std::map &IdentityMap) { + BasicBlock *CurFunclet = FuncletPath.back(); + // Copy the children vector because we might changing it. + std::vector Children(FuncletChildren[CurFunclet]); + for (BasicBlock *ChildFunclet : Children) { + // Don't allow the funclet chain to unwind back on itself. + // If this funclet is already in the current funclet chain, make the + // path to it through the current funclet unreachable. + bool IsCyclic = false; + BasicBlock *ChildIdentity = IdentityMap[ChildFunclet]; + if (!ChildIdentity) + IdentityMap[ChildFunclet] = ChildIdentity = ChildFunclet; + for (BasicBlock *Ancestor : FuncletPath) { + BasicBlock *AncestorIdentity = IdentityMap[Ancestor]; + if (!AncestorIdentity) + IdentityMap[Ancestor] = AncestorIdentity = Ancestor; + if (AncestorIdentity == ChildIdentity) { + IsCyclic = true; + break; + } + } + // If the unwind chain wraps back on itself, break the chain. + if (IsCyclic) { + makeFuncletEdgeUnreachable(CurFunclet, ChildFunclet); + continue; + } + // If this child funclet has other parents, clone the entire funclet. + if (FuncletParents[ChildFunclet].size() > 1) { + ChildFunclet = cloneFuncletForParent(F, ChildFunclet, CurFunclet); + IdentityMap[ChildFunclet] = ChildIdentity; + } + FuncletPath.push_back(ChildFunclet); + resolveFuncletAncestryForPath(F, FuncletPath, IdentityMap); + FuncletPath.pop_back(); + } +} + void WinEHPrepare::demotePHIsOnFunclets(Function &F) { // Strip PHI nodes off of EH pads. SmallVector PHINodes; @@ -3260,6 +3602,8 @@ cloneCommonBlocks(F, EntryBlocks); + resolveFuncletAncestry(F); + if (!DisableCleanups) { removeImplausibleTerminators(F); @@ -3271,6 +3615,7 @@ BlockColors.clear(); FuncletBlocks.clear(); FuncletChildren.clear(); + FuncletParents.clear(); return true; } Index: lib/Transforms/Utils/Local.cpp =================================================================== --- lib/Transforms/Utils/Local.cpp +++ lib/Transforms/Utils/Local.cpp @@ -595,6 +595,31 @@ PredBB->eraseFromParent(); } + +void llvm::convertInvokeToCall(InvokeInst *II) { + SmallVector Args(II->op_begin(), II->op_end() - 3); + // Insert a call instruction before the invoke. + CallInst *Call = CallInst::Create(II->getCalledValue(), Args, "", II); + Call->takeName(II); + Call->setCallingConv(II->getCallingConv()); + Call->setAttributes(II->getAttributes()); + Call->setDebugLoc(II->getDebugLoc()); + + // Anything that used the value produced by the invoke instruction now uses + // the value produced by the call instruction. Note that we do this even + // for void functions and calls with no uses so that the callgraph edge is + // updated. + II->replaceAllUsesWith(Call); + II->getUnwindDest()->removePredecessor(II->getParent()); + + // Insert a branch to the normal destination right before the invoke. + BranchInst::Create(II->getNormalDest(), II); + + // Finally, delete the invoke instruction! + II->eraseFromParent(); +} + + /// CanMergeValues - Return true if we can choose one of these values to use /// in place of the other. Note that we will always choose the non-undef /// value to keep. @@ -1193,24 +1218,6 @@ } } -/// changeToCall - Convert the specified invoke into a normal call. -static void changeToCall(InvokeInst *II) { - SmallVector Args(II->op_begin(), II->op_end() - 3); - CallInst *NewCall = CallInst::Create(II->getCalledValue(), Args, "", II); - NewCall->takeName(II); - NewCall->setCallingConv(II->getCallingConv()); - NewCall->setAttributes(II->getAttributes()); - NewCall->setDebugLoc(II->getDebugLoc()); - II->replaceAllUsesWith(NewCall); - - // Follow the call by a branch to the normal destination. - BranchInst::Create(II->getNormalDest(), II); - - // Update PHI nodes in the unwind destination - II->getUnwindDest()->removePredecessor(II->getParent()); - II->eraseFromParent(); -} - static bool markAliveBlocks(Function &F, SmallPtrSetImpl &Reachable) { @@ -1293,7 +1300,7 @@ II->getUnwindDest()->removePredecessor(II->getParent()); II->eraseFromParent(); } else - changeToCall(II); + convertInvokeToCall(II); Changed = true; } } @@ -1310,7 +1317,7 @@ TerminatorInst *TI = BB->getTerminator(); if (auto *II = dyn_cast(TI)) { - changeToCall(II); + convertInvokeToCall(II); return; } Index: test/CodeGen/WinEH/wineh-cloning.ll =================================================================== --- test/CodeGen/WinEH/wineh-cloning.ll +++ test/CodeGen/WinEH/wineh-cloning.ll @@ -280,7 +280,30 @@ ; the dynamic path enters %left, then enters %inner, ; then calls @h, and that the call to @h doesn't return. ; CHECK-LABEL: define void @test6( -; TODO: CHECKs +; CHECK: left: +; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: %x = call i32 @g() +; CHECK: store i32 %x, i32* %x.wineh.spillslot +; CHECK: to label %shared.cont unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: shared.cont: +; CHECK: unreachable +; CHECK: [[SHARED_CONT_LEFT]]: +; CHECK: unreachable +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_R:\%.+]] = cleanuppad [] +; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_R]]) +; CHECK: cleanupret [[I_R:\%.+]] unwind label %right.end +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_L:\%.+]] = cleanuppad [] +; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot +; CHECK: call void @h(i32 [[X_RELOAD_L]]) +; CHECK: unreachable define void @test7() personality i32 (...)* @__CxxFrameHandler3 { @@ -312,7 +335,32 @@ ; with the join at the entry itself instead of following a ; non-pad join. ; CHECK-LABEL: define void @test7( -; TODO: CHECKs +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %right +; CHECK: left: +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: to label %unreachable unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_RIGHT]]: +; CHECK: [[I_R:\%.+]] = cleanuppad [] +; CHECK: [[X_R:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X_R]]) +; CHECK: cleanupret [[I_R]] unwind label %right.end +; CHECK: [[INNER_LEFT]]: +; CHECK: [[I_L:\%.+]] = cleanuppad [] +; CHECK: [[X_L:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X_L]]) +; CHECK: unreachable +; CHECK: unreachable: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable define void @test8() personality i32 (...)* @__CxxFrameHandler3 { @@ -350,7 +398,40 @@ ; %inner is a two-parent child which itself has a child; need ; to make two copies of both the %inner and %inner.child. ; CHECK-LABEL: define void @test8( -; TODO: CHECKs +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %right +; CHECK: left: +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_LEFT:.+]] +; CHECK: right: +; CHECK: to label %right.catch unwind label %right.end +; CHECK: right.catch: +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]] +; CHECK: right.end: +; CHECK: catchendpad unwind to caller +; CHECK: [[INNER_RIGHT]]: +; CHECK: to label %[[UNREACHABLE_INNER_RIGHT:.+]] unwind label %[[INNER_CHILD_RIGHT:.+]] +; CHECK: [[INNER_LEFT]]: +; CHECK: to label %[[UNREACHABLE_INNER_LEFT:.+]] unwind label %[[INNER_CHILD_LEFT:.+]] +; CHECK: [[INNER_CHILD_RIGHT]]: +; CHECK: [[TMP:\%.+]] = cleanuppad [] +; CHECK: [[X:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X]]) +; CHECK: unreachable +; CHECK: [[INNER_CHILD_LEFT]]: +; CHECK: [[TMP:\%.+]] = cleanuppad [] +; CHECK: [[X:\%.+]] = call i32 @g() +; CHECK: call void @h(i32 [[X]]) +; CHECK: unreachable +; CHECK: [[UNREACHABLE_INNER_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_INNER_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable define void @test9() personality i32 (...)* @__CxxFrameHandler3 { @@ -383,7 +464,43 @@ ; of which was which along the way; generating each possibility lets ; whichever case was correct execute correctly. ; CHECK-LABEL: define void @test9( -; TODO: CHECKs +; CHECK: entry: +; CHECK: to label %invoke.cont unwind label %[[LEFT:.+]] +; CHECK: invoke.cont: +; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %[[RIGHT:.+]] +; CHECK: [[LEFT_FROM_RIGHT_UNREACHABLE:.+]]: +; CHECK-NEXT: cleanuppad [] +; CHECK-NEXT: unreachable +; CHECK: [[LEFT_FROM_RIGHT:.+]]: +; CHECK: call void @h(i32 1) +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_LEFT_FROM_RIGHT:.+]] unwind label %[[RIGHT_FROM_LEFT_UNREACHABLE:.+]] +; CHECK: [[LEFT]]: +; CHECK: call void @h(i32 1) +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[RIGHT_FROM_LEFT:.+]] +; CHECK: [[RIGHT_FROM_LEFT_UNREACHABLE]]: +; CHECK-NEXT: cleanuppad [] +; CHECK-NEXT: unreachable +; CHECK: [[RIGHT]]: +; CHECK: call void @h(i32 2) +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[LEFT_FROM_RIGHT]] +; CHECK: [[RIGHT_FROM_LEFT]]: +; CHECK: call void @h(i32 2) +; CHECK: invoke void @f() +; CHECK: to label %[[UNREACHABLE_RIGHT_FROM_LEFT:.+]] unwind label %[[LEFT_FROM_RIGHT_UNREACHABLE]] +; CHECK: [[UNREACHABLE_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_RIGHT_FROM_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT_FROM_RIGHT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_LEFT]]: +; CHECK: unreachable +; CHECK: [[UNREACHABLE_ENTRY]]: +; CHECK: unreachable + define void @test10() personality i32 (...)* @__CxxFrameHandler3 { entry: Index: test/CodeGen/WinEH/wineh-demotion.ll =================================================================== --- test/CodeGen/WinEH/wineh-demotion.ll +++ test/CodeGen/WinEH/wineh-demotion.ll @@ -86,18 +86,18 @@ ; CHECK: store i32 %z ; CHECK-NEXT: invoke void @f invoke void @f() - to label %catchret.inner unwind label %merge.outer + to label %catchret.inner unwind label %catchend.inner catchret.inner: catchret %cpinner to label %exit catchend.inner: + ; CHECK-NOT: = phi + %y = phi i32 [ %x, %merge.inner ], [ %z, %catch.inner ] catchendpad unwind label %merge.outer merge.outer: ; CHECK: merge.outer: - ; CHECK-NOT: = phi ; CHECK: [[CatchPad:%[^ ]+]] = catchpad [] - %y = phi i32 [ %x, %catchend.inner ], [ %z, %catch.inner ] %cpouter = catchpad [] to label %catch.outer unwind label %catchend.outer catchend.outer: Index: test/CodeGen/WinEH/wineh-no-demotion.ll =================================================================== --- test/CodeGen/WinEH/wineh-no-demotion.ll +++ test/CodeGen/WinEH/wineh-no-demotion.ll @@ -39,12 +39,20 @@ unreachable inner: - ; CHECK: %phi = phi i32 [ %x, %right ], [ 0, %invoke.cont2 ], [ %x.for.left, %left ] %phi = phi i32 [ %x, %shared ], [ 0, %invoke.cont2 ] %i = cleanuppad [] call void @h(i32 %phi) unreachable +; CHECK [[INNER_INVOKE_CONT2:inner.*]]: + ; CHECK: call void @h(i32 0) + +; CHECK [[INNER_RIGHT:inner.*]]: + ; CHECK: call void @h(i32 %x) + +; CHECK [[INNER_LEFT:inner.*]]: + ; CHECK: call void @h(i32 %x.for.left) + exit: unreachable } @@ -76,12 +84,16 @@ unreachable inner: - ; CHECK: %x1 = phi i32 [ %x.for.left, %left ], [ %x, %right ] - ; CHECK: call void @h(i32 %x1) %i = cleanuppad [] call void @h(i32 %x) unreachable +; CHECK [[INNER_RIGHT:inner.*]]: + ; CHECK: call void @h(i32 %x) + +; CHECK [[INNER_LEFT:inner.*]]: + ; CHECK: call void @h(i32 %x.for.left) + exit: unreachable }