[llvm] r246896 - [WinEH] Teach SimplfyCFG to eliminate empty cleanup pads.

Andrew Kaylor via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 4 16:39:40 PDT 2015


Author: akaylor
Date: Fri Sep  4 18:39:40 2015
New Revision: 246896

URL: http://llvm.org/viewvc/llvm-project?rev=246896&view=rev
Log:
[WinEH] Teach SimplfyCFG to eliminate empty cleanup pads.

Differential Revision: http://reviews.llvm.org/D12434


Added:
    llvm/trunk/test/Transforms/SimplifyCFG/empty-cleanuppad.ll
Modified:
    llvm/trunk/lib/Transforms/Utils/SimplifyCFG.cpp

Modified: llvm/trunk/lib/Transforms/Utils/SimplifyCFG.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/SimplifyCFG.cpp?rev=246896&r1=246895&r2=246896&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Utils/SimplifyCFG.cpp (original)
+++ llvm/trunk/lib/Transforms/Utils/SimplifyCFG.cpp Fri Sep  4 18:39:40 2015
@@ -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(Br
   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<Value*, 8> 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,29 +2944,181 @@ bool SimplifyCFGOpt::SimplifyResume(Resu
   // 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<InvokeInst>((*PI++)->getTerminator());
-    SmallVector<Value*, 8> 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());
+    convertInvokeToCall(II);
+  }
+
+  // The landingpad is now unreachable.  Zap it.
+  BB->eraseFromParent();
+  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<CleanupPadInst>(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<DbgInfoIntrinsic>(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<PHINode>(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<PHINode>(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);
+        }
+      }
+    }
 
-    // Insert a branch to the normal destination right before the invoke.
-    BranchInst::Create(II->getNormalDest(), II);
+    // 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<PHINode>(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;
 
-    // Finally, delete the invoke instruction!
-    II->eraseFromParent();
+      // 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);
+    }
   }
 
-  // The landingpad is now unreachable.  Zap it.
+  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<InvokeInst>(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<CleanupReturnInst>(TI)) {
+          auto *NewCRI = CleanupReturnInst::Create(CRI->getCleanupPad(),
+                                                   nullptr, CRI);
+          NewCRI->takeName(CRI);
+          NewCRI->setDebugLoc(CRI->getDebugLoc());
+          CRI->eraseFromParent();
+        } else if (auto *CEP = dyn_cast<CatchEndPadInst>(TI)) {
+          auto *NewCEP = CatchEndPadInst::Create(CEP->getContext(), nullptr,
+                                                 CEP);
+          NewCEP->takeName(CEP);
+          NewCEP->setDebugLoc(CEP->getDebugLoc());
+          CEP->eraseFromParent();
+        } else if (auto *TPI = dyn_cast<TerminatePadInst>(TI)) {
+          SmallVector<Value *, 3> 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<CatchPadInst>(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<InvokeInst>(TI))
+        II->setUnwindDest(UnwindDest);
+      else if (auto *CRI = dyn_cast<CleanupReturnInst>(TI))
+        CRI->setUnwindDest(UnwindDest);
+      else if (auto *CEP = dyn_cast<CatchEndPadInst>(TI))
+        CEP->setUnwindDest(UnwindDest);
+      else if (auto *TPI = dyn_cast<TerminatePadInst>(TI))
+        TPI->setUnwindDest(UnwindDest);
+      else if (auto *CPI = dyn_cast<CatchPadInst>(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;
 }
@@ -4679,6 +4857,9 @@ bool SimplifyCFGOpt::run(BasicBlock *BB)
     if (SimplifyReturn(RI, Builder)) return true;
   } else if (ResumeInst *RI = dyn_cast<ResumeInst>(BB->getTerminator())) {
     if (SimplifyResume(RI, Builder)) return true;
+  } else if (CleanupReturnInst *RI =
+               dyn_cast<CleanupReturnInst>(BB->getTerminator())) {
+    if (SimplifyCleanupReturn(RI)) return true;
   } else if (SwitchInst *SI = dyn_cast<SwitchInst>(BB->getTerminator())) {
     if (SimplifySwitch(SI, Builder)) return true;
   } else if (UnreachableInst *UI =

Added: llvm/trunk/test/Transforms/SimplifyCFG/empty-cleanuppad.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/SimplifyCFG/empty-cleanuppad.ll?rev=246896&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/SimplifyCFG/empty-cleanuppad.ll (added)
+++ llvm/trunk/test/Transforms/SimplifyCFG/empty-cleanuppad.ll Fri Sep  4 18:39:40 2015
@@ -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 at 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 at 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 at 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 at 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 at 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 at 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 at XZ"(%struct.S2*)
+declare void @g()
+declare void @use_x(i32 %x)
+
+declare i32 @__CxxFrameHandler3(...)
+




More information about the llvm-commits mailing list