From 580f34a8838b0fa4c61af54dac2ee2fb47576bf7 Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Fri, 4 Sep 2015 23:39:40 +0000 Subject: [PATCH] [WinEH] Teach SimplfyCFG to eliminate empty cleanup pads. Differential Revision: http://reviews.llvm.org/D12434 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@246896 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Transforms/Utils/SimplifyCFG.cpp | 221 +++++++- .../SimplifyCFG/empty-cleanuppad.ll | 486 ++++++++++++++++++ 2 files changed, 687 insertions(+), 20 deletions(-) create mode 100644 test/Transforms/SimplifyCFG/empty-cleanuppad.ll diff --git a/lib/Transforms/Utils/SimplifyCFG.cpp b/lib/Transforms/Utils/SimplifyCFG.cpp index c6312a3a4e8..534c738f832 100644 --- a/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/lib/Transforms/Utils/SimplifyCFG.cpp @@ -124,6 +124,7 @@ class SimplifyCFGOpt { bool SimplifyReturn(ReturnInst *RI, IRBuilder<> &Builder); bool SimplifyResume(ResumeInst *RI, IRBuilder<> &Builder); + bool SimplifyCleanupReturn(CleanupReturnInst *RI); bool SimplifyUnreachable(UnreachableInst *UI); bool SimplifySwitch(SwitchInst *SI, IRBuilder<> &Builder); bool SimplifyIndirectBr(IndirectBrInst *IBI); @@ -2899,6 +2900,31 @@ static bool SimplifyBranchOnICmpChain(BranchInst *BI, IRBuilder<> &Builder, return true; } +// FIXME: This seems like a pretty common thing to want to do. Consider +// whether there is a more accessible place to put this. +static void 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(); +} + bool SimplifyCFGOpt::SimplifyResume(ResumeInst *RI, IRBuilder<> &Builder) { // If this is a trivial landing pad that just continues unwinding the caught // exception then zap the landing pad, turning its invokes into calls. @@ -2918,26 +2944,7 @@ bool SimplifyCFGOpt::SimplifyResume(ResumeInst *RI, IRBuilder<> &Builder) { // Turn all invokes that unwind here into calls and delete the basic block. for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE;) { InvokeInst *II = cast((*PI++)->getTerminator()); - 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); - BB->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(); + convertInvokeToCall(II); } // The landingpad is now unreachable. Zap it. @@ -2945,6 +2952,177 @@ bool SimplifyCFGOpt::SimplifyResume(ResumeInst *RI, IRBuilder<> &Builder) { return true; } +bool SimplifyCFGOpt::SimplifyCleanupReturn(CleanupReturnInst *RI) { + // If this is a trivial cleanup pad that executes no instructions, it can be + // eliminated. If the cleanup pad continues to the caller, any predecessor + // that is an EH pad will be updated to continue to the caller and any + // predecessor that terminates with an invoke instruction will have its invoke + // instruction converted to a call instruction. If the cleanup pad being + // simplified does not continue to the caller, each predecessor will be + // updated to continue to the unwind destination of the cleanup pad being + // simplified. + BasicBlock *BB = RI->getParent(); + Instruction *CPInst = dyn_cast(BB->getFirstNonPHI()); + if (!CPInst) + // This isn't an empty cleanup. + return false; + + // Check that there are no other instructions except for debug intrinsics. + BasicBlock::iterator I = CPInst, E = RI; + while (++I != E) + if (!isa(I)) + return false; + + // If the cleanup return we are simplifying unwinds to the caller, this + // will set UnwindDest to nullptr. + BasicBlock *UnwindDest = RI->getUnwindDest(); + + // We're about to remove BB from the control flow. Before we do, sink any + // PHINodes into the unwind destination. Doing this before changing the + // control flow avoids some potentially slow checks, since we can currently + // be certain that UnwindDest and BB have no common predecessors (since they + // are both EH pads). + if (UnwindDest) { + // First, go through the PHI nodes in UnwindDest and update any nodes that + // reference the block we are removing + for (BasicBlock::iterator I = UnwindDest->begin(), + IE = UnwindDest->getFirstNonPHI(); + I != IE; ++I) { + PHINode *DestPN = cast(I); + + unsigned Idx = DestPN->getBasicBlockIndex(BB); + // Since BB unwinds to UnwindDest, it has to be in the PHI node. + assert(Idx != -1); + // This PHI node has an incoming value that corresponds to a control + // path through the cleanup pad we are removing. If the incoming + // value is in the cleanup pad, it must be a PHINode (because we + // verified above that the block is otherwise empty). Otherwise, the + // value is either a constant or a value that dominates the cleanup + // pad being removed. + // + // Because BB and UnwindDest are both EH pads, all of their + // predecessors must unwind to these blocks, and since no instruction + // can have multiple unwind destinations, there will be no overlap in + // incoming blocks between SrcPN and DestPN. + Value *SrcVal = DestPN->getIncomingValue(Idx); + PHINode *SrcPN = dyn_cast(SrcVal); + + // Remove the entry for the block we are deleting. + DestPN->removeIncomingValue(Idx, false); + + if (SrcPN && SrcPN->getParent() == BB) { + // If the incoming value was a PHI node in the cleanup pad we are + // removing, we need to merge that PHI node's incoming values into + // DestPN. + for (unsigned SrcIdx = 0, SrcE = SrcPN->getNumIncomingValues(); + SrcIdx != SrcE; ++SrcIdx) { + DestPN->addIncoming(SrcPN->getIncomingValue(SrcIdx), + SrcPN->getIncomingBlock(SrcIdx)); + } + } else { + // Otherwise, the incoming value came from above BB and + // so we can just reuse it. We must associate all of BB's + // predecessors with this value. + for (auto *pred : predecessors(BB)) { + DestPN->addIncoming(SrcVal, pred); + } + } + } + + // Sink any remaining PHI nodes directly into UnwindDest. + Instruction *InsertPt = UnwindDest->getFirstNonPHI(); + for (BasicBlock::iterator I = BB->begin(), IE = BB->getFirstNonPHI(); + I != IE;) { + // The iterator must be incremented here because the instructions are + // being moved to another block. + PHINode *PN = cast(I++); + if (PN->use_empty()) + // If the PHI node has no uses, just leave it. It will be erased + // when we erase BB below. + continue; + + // Otherwise, sink this PHI node into UnwindDest. + // Any predecessors to UnwindDest which are not already represented + // must be back edges which inherit the value from the path through + // BB. In this case, the PHI value must reference itself. + for (auto *pred : predecessors(UnwindDest)) + if (pred != BB) + PN->addIncoming(PN, pred); + PN->moveBefore(InsertPt); + } + } + + for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE;) { + // The iterator must be updated here because we are removing this pred. + BasicBlock *PredBB = *PI++; + TerminatorInst *TI = PredBB->getTerminator(); + if (UnwindDest == nullptr) { + if (auto *II = dyn_cast(TI)) { + // The cleanup return being simplified continues to the caller and this + // predecessor terminated with an invoke instruction. Convert the + // invoke to a call. + // This call updates the predecessor/successor chain. + convertInvokeToCall(II); + } else { + // In the remaining cases the predecessor's terminator unwinds to the + // block we are removing. We need to create a new instruction that + // unwinds to the caller. Simply setting the unwind destination to + // nullptr would leave the objects internal data in an inconsistent + // state. + // FIXME: Consider whether it is better to update setUnwindDest to + // keep things consistent. + if (auto *CRI = dyn_cast(TI)) { + auto *NewCRI = CleanupReturnInst::Create(CRI->getCleanupPad(), + nullptr, CRI); + NewCRI->takeName(CRI); + NewCRI->setDebugLoc(CRI->getDebugLoc()); + CRI->eraseFromParent(); + } else if (auto *CEP = dyn_cast(TI)) { + auto *NewCEP = CatchEndPadInst::Create(CEP->getContext(), nullptr, + CEP); + NewCEP->takeName(CEP); + NewCEP->setDebugLoc(CEP->getDebugLoc()); + CEP->eraseFromParent(); + } else if (auto *TPI = dyn_cast(TI)) { + SmallVector TerminatePadArgs; + for (Value *Operand : TPI->arg_operands()) + TerminatePadArgs.push_back(Operand); + auto *NewTPI = TerminatePadInst::Create(TPI->getContext(), nullptr, + TerminatePadArgs, TPI); + NewTPI->takeName(TPI); + NewTPI->setDebugLoc(TPI->getDebugLoc()); + TPI->eraseFromParent(); + } else if (auto *CPI = dyn_cast(TI)) { + llvm_unreachable("A catchpad may not unwind to a cleanuppad."); + } else { + llvm_unreachable("Unexpected predecessor to cleanup pad."); + } + } + } else { + // If the predecessor did not terminate with an invoke instruction, it + // must be some variety of EH pad. + TerminatorInst *TI = PredBB->getTerminator(); + // FIXME: Introducing an EH terminator base class would simplify this. + if (auto *II = dyn_cast(TI)) + II->setUnwindDest(UnwindDest); + else if (auto *CRI = dyn_cast(TI)) + CRI->setUnwindDest(UnwindDest); + else if (auto *CEP = dyn_cast(TI)) + CEP->setUnwindDest(UnwindDest); + else if (auto *TPI = dyn_cast(TI)) + TPI->setUnwindDest(UnwindDest); + else if (auto *CPI = dyn_cast(TI)) + llvm_unreachable("A catchpad may not unwind to a cleanuppad."); + else + llvm_unreachable("Unexpected predecessor to cleanup pad."); + } + } + + // The cleanup pad is now unreachable. Zap it. + BB->eraseFromParent(); + return true; +} + bool SimplifyCFGOpt::SimplifyReturn(ReturnInst *RI, IRBuilder<> &Builder) { BasicBlock *BB = RI->getParent(); if (!BB->getFirstNonPHIOrDbg()->isTerminator()) return false; @@ -4679,6 +4857,9 @@ bool SimplifyCFGOpt::run(BasicBlock *BB) { if (SimplifyReturn(RI, Builder)) return true; } else if (ResumeInst *RI = dyn_cast(BB->getTerminator())) { if (SimplifyResume(RI, Builder)) return true; + } else if (CleanupReturnInst *RI = + dyn_cast(BB->getTerminator())) { + if (SimplifyCleanupReturn(RI)) return true; } else if (SwitchInst *SI = dyn_cast(BB->getTerminator())) { if (SimplifySwitch(SI, Builder)) return true; } else if (UnreachableInst *UI = diff --git a/test/Transforms/SimplifyCFG/empty-cleanuppad.ll b/test/Transforms/SimplifyCFG/empty-cleanuppad.ll new file mode 100644 index 00000000000..0b1d41fc1b8 --- /dev/null +++ b/test/Transforms/SimplifyCFG/empty-cleanuppad.ll @@ -0,0 +1,486 @@ +; RUN: opt < %s -simplifycfg -S | filecheck %s + +; ModuleID = 'cppeh-simplify.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc18.0.0" + + +; This case arises when two objects with empty destructors are cleaned up. +; +; void f1() { +; S a; +; S b; +; g(); +; } +; +; In this case, both cleanup pads can be eliminated and the invoke can be +; converted to a call. +; +; CHECK: define void @f1() +; CHECK: entry: +; CHECK: call void @g() +; CHECK: ret void +; CHECK-NOT: cleanuppad +; CHECK: } +; +define void @f1() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + ret void + +ehcleanup: ; preds = %entry + %0 = cleanuppad [] + cleanupret %0 unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %ehcleanup + %1 = cleanuppad [] + cleanupret %1 unwind to caller +} + + +; This case arises when an object with an empty destructor must be cleaned up +; outside of a try-block and an object with a non-empty destructor must be +; cleaned up within the try-block. +; +; void f2() { +; S a; +; try { +; S2 b; +; g(); +; } catch (...) {} +; } +; +; In this case, the outermost cleanup pad can be eliminated and the catch block +; should unwind to the caller (that is, exception handling continues with the +; parent frame of the caller). +; +; CHECK: define void @f2() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: ehcleanup: +; CHECK: cleanuppad +; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) +; CHECK: cleanupret %0 unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK: catchpad +; CHECK: catch: +; CHECK: catchret +; CHECK: catchendblock: ; preds = %catch.dispatch +; CHECK: catchendpad unwind to caller +; CHECK-NOT: cleanuppad +; CHECK: } +; +define void @f2() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %b = alloca %struct.S2, align 1 + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + br label %try.cont + +ehcleanup: ; preds = %entry + %0 = cleanuppad [] + call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) + cleanupret %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup + %1 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + catchret %1 to label %catchret.dest + +catchret.dest: ; preds = %catch + br label %try.cont + +try.cont: ; preds = %catchret.dest, %invoke.cont + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %catchendblock + %2 = cleanuppad [] + cleanupret %2 unwind to caller +} + + +; This case arises when an object with a non-empty destructor must be cleaned up +; outside of a try-block and an object with an empty destructor must be cleaned +; within the try-block. +; +; void f3() { +; S2 a; +; try { +; S b; +; g(); +; } catch (...) {} +; } +; +; In this case the inner cleanup pad should be eliminated and the invoke of g() +; should unwind directly to the catchpad. +; +; CHECK: define void @f3() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: to label %try.cont unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK: catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock +; CHECK: catch: +; CHECK: catchret +; CHECK: catchendblock: +; CHECK: catchendpad unwind label %ehcleanup.1 +; CHECK: ehcleanup.1: +; CHECK: cleanuppad +; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) +; CHECK: cleanupret %1 unwind to caller +; CHECK: } +; +define void @f3() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %a = alloca %struct.S2, align 1 + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + br label %try.cont + +ehcleanup: ; preds = %entry + %0 = cleanuppad [] + cleanupret %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup + %1 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + catchret %1 to label %catchret.dest + +catchret.dest: ; preds = %catch + br label %try.cont + +try.cont: ; preds = %catchret.dest, %invoke.cont + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %catchendblock + %2 = cleanuppad [] + call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) + cleanupret %2 unwind to caller +} + + +; This case arises when an object with an empty destructor may require cleanup +; from either inside or outside of a try-block. +; +; void f4() { +; S a; +; g(); +; try { +; g(); +; } catch (...) {} +; } +; +; In this case, the cleanuppad should be eliminated, the invoke outside of the +; call block should be converted to a call and the catchendpad should unwind +; to the caller (that is, that is, exception handling continues with the parent +; frame of the caller).) +; +; CHECK: define void @f4() +; CHECK: entry: +; CHECK: call void @g +; Note: The cleanuppad simplification will insert an unconditional branch here +; but it will be eliminated, placing the following invoke in the entry BB. +; CHECK: invoke void @g() +; CHECK: to label %try.cont unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK: catchpad +; CHECK: catch: +; CHECK: catchret +; CHECK: catchendblock: +; CHECK: catchendpad unwind to caller +; CHECK-NOT: cleanuppad +; CHECK: } +; +define void @f4() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + invoke void @g() + to label %try.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %invoke.cont + %0 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + catchret %0 to label %try.cont + +try.cont: ; preds = %catch, %invoke.cont + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind label %ehcleanup + +ehcleanup: ; preds = %catchendblock, %entry + %1 = cleanuppad [] + cleanupret %1 unwind to caller +} + +; This tests the case where a terminatepad unwinds to a cleanuppad. +; I'm not sure how this case would arise, but it seems to be syntactically +; legal so I'm testing it. +; +; CHECK: define void @f5() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: to label %try.cont unwind label %terminate +; CHECK: terminate: +; CHECK: terminatepad [i7 4] unwind to caller +; CHECK-NOT: cleanuppad +; CHECK: try.cont: +; CHECK: invoke void @g() +; CHECK: to label %try.cont.1 unwind label %terminate.1 +; CHECK: terminate.1: +; CHECK: terminatepad [i7 4] unwind label %ehcleanup.2 +; CHECK-NOT: ehcleanup.1: +; CHECK: ehcleanup.2: +; CHECK: [[TMP:\%.+]] = cleanuppad +; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) +; CHECK: cleanupret [[TMP]] unwind to caller +; CHECK: } +define void @f5() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %a = alloca %struct.S2, align 1 + invoke void @g() + to label %try.cont unwind label %terminate + +terminate: ; preds = %entry + terminatepad [i7 4] unwind label %ehcleanup + +ehcleanup: ; preds = %terminate + %0 = cleanuppad [] + cleanupret %0 unwind to caller + +try.cont: ; preds = %entry + invoke void @g() + to label %try.cont.1 unwind label %terminate.1 + +terminate.1: ; preds = %try.cont + terminatepad [i7 4] unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %terminate.1 + %1 = cleanuppad [] + cleanupret %1 unwind label %ehcleanup.2 + +ehcleanup.2: ; preds = %ehcleanup.1 + %2 = cleanuppad [] + call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) + cleanupret %2 unwind to caller + +try.cont.1: ; preds = %try.cont + ret void +} + +; This case tests simplification of an otherwise empty cleanup pad that contains +; a PHI node. +; +; int f6() { +; int state = 1; +; try { +; S a; +; g(); +; state = 2; +; g(); +; } catch (...) { +; return state; +; } +; return 0; +; } +; +; In this case, the cleanup pad should be eliminated and the PHI node in the +; cleanup pad should be sunk into the catch dispatch block. +; +; CHECK: define i32 @f6() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: invoke.cont: +; CHECK: invoke void @g() +; CHECK-NOT: ehcleanup: +; CHECK-NOT: cleanuppad +; CHECK: catch.dispatch: +; CHECK: %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] +; CHECK: } +define i32 @f6() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + invoke void @g() + to label %return unwind label %ehcleanup + +ehcleanup: ; preds = %invoke.cont, %entry + %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] + %0 = cleanuppad [] + cleanupret %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup + %1 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + catchret %1 to label %return + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind to caller + +return: ; preds = %invoke.cont, %catch + %retval.0 = phi i32 [ %state.0, %catch ], [ 0, %invoke.cont ] + ret i32 %retval.0 +} + +; This case tests another variation of simplification of an otherwise empty +; cleanup pad that contains a PHI node. +; +; int f7() { +; int state = 1; +; try { +; g(); +; state = 2; +; S a; +; g(); +; state = 3; +; g(); +; } catch (...) { +; return state; +; } +; return 0; +; } +; +; In this case, the cleanup pad should be eliminated and the PHI node in the +; cleanup pad should be merged with the PHI node in the catch dispatch block. +; +; CHECK: define i32 @f7() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: invoke.cont: +; CHECK: invoke void @g() +; CHECK: invoke.cont.1: +; CHECK: invoke void @g() +; CHECK-NOT: ehcleanup: +; CHECK-NOT: cleanuppad +; CHECK: catch.dispatch: +; CHECK: %state.1 = phi i32 [ 1, %entry ], [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] +; CHECK: } +define i32 @f7() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %catch.dispatch + +invoke.cont: ; preds = %entry + invoke void @g() + to label %invoke.cont.1 unwind label %ehcleanup + +invoke.cont.1: ; preds = %invoke.cont + invoke void @g() + to label %return unwind label %ehcleanup + +ehcleanup: ; preds = %invoke.cont.1, %invoke.cont + %state.0 = phi i32 [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] + %0 = cleanuppad [] + cleanupret %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup, %entry + %state.1 = phi i32 [ %state.0, %ehcleanup ], [ 1, %entry ] + %1 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + catchret %1 to label %return + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind to caller + +return: ; preds = %invoke.cont.1, %catch + %retval.0 = phi i32 [ %state.1, %catch ], [ 0, %invoke.cont.1 ] + ret i32 %retval.0 +} + +; This case tests a scenario where an empty cleanup pad is not dominated by all +; of the predecessors of its successor, but the successor references a PHI node +; in the empty cleanup pad. +; +; Conceptually, the case being modeled is something like this: +; +; int f8() { +; int x = 1; +; try { +; S a; +; g(); +; x = 2; +; retry: +; g(); +; return +; } catch (...) { +; use_x(x); +; } +; goto retry; +; } +; +; While that C++ syntax isn't legal, the IR below is. +; +; In this case, the PHI node that is sunk from ehcleanup to catch.dispatch +; should have an incoming value entry for path from 'foo' that references the +; PHI node itself. +; +; CHECK: define void @f8() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: invoke.cont: +; CHECK: invoke void @g() +; CHECK-NOT: ehcleanup: +; CHECK-NOT: cleanuppad +; CHECK: catch.dispatch: +; CHECK: %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ], [ %x, %catch.cont ] +; CHECK: } +define void @f8() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + invoke void @g() + to label %return unwind label %ehcleanup + +ehcleanup: ; preds = %invoke.cont, %entry + %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] + %0 = cleanuppad [] + cleanupret %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup, %catch.cont + %1 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + call void @use_x(i32 %x) + catchret %1 to label %catch.cont + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind to caller + +catch.cont: ; preds = %catch + invoke void @g() + to label %return unwind label %catch.dispatch + +return: ; preds = %invoke.cont, %catch.cont + ret void +} + +%struct.S = type { i8 } +%struct.S2 = type { i8 } +declare void @"\01??1S2@@QEAA@XZ"(%struct.S2*) +declare void @g() +declare void @use_x(i32 %x) + +declare i32 @__CxxFrameHandler3(...) + -- 2.34.1