[WinEH] Split blocks at calls to llvm.eh.begincatch
authorAndrew Kaylor <andrew.kaylor@intel.com>
Tue, 28 Apr 2015 21:54:14 +0000 (21:54 +0000)
committerAndrew Kaylor <andrew.kaylor@intel.com>
Tue, 28 Apr 2015 21:54:14 +0000 (21:54 +0000)
Differential Revision: http://reviews.llvm.org/D9311

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@236046 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/WinEHPrepare.cpp
test/CodeGen/WinEH/cppeh-demote-liveout.ll
test/CodeGen/WinEH/cppeh-mixed-catch-and-cleanup.ll [new file with mode: 0644]
test/CodeGen/WinEH/cppeh-shared-empty-catch.ll
test/CodeGen/WinEH/seh-catch-all.ll
test/CodeGen/WinEH/seh-simple.ll

index 1325821d498e71dc96158c74aa5faf64af124c82..82f40371f49b90cca34a31e9dd7d9a7c9ef6cd92 100644 (file)
@@ -89,6 +89,10 @@ private:
   void promoteLandingPadValues(LandingPadInst *LPad);
   void demoteValuesLiveAcrossHandlers(Function &F,
                                       SmallVectorImpl<LandingPadInst *> &LPads);
+  void findSEHEHReturnPoints(Function &F,
+                             SetVector<BasicBlock *> &EHReturnBlocks);
+  void findCXXEHReturnPoints(Function &F,
+                             SetVector<BasicBlock *> &EHReturnBlocks);
   void completeNestedLandingPad(Function *ParentFn,
                                 LandingPadInst *OutlinedLPad,
                                 const LandingPadInst *OriginalLPad,
@@ -392,13 +396,62 @@ static void findReachableBlocks(SmallPtrSetImpl<BasicBlock *> &ReachableBBs,
   }
 }
 
+// Attempt to find an instruction where a block can be split before
+// a call to llvm.eh.begincatch and its operands.  If the block
+// begins with the begincatch call or one of its adjacent operands
+// the block will not be split.
+static Instruction *findBeginCatchSplitPoint(BasicBlock *BB,
+                                             IntrinsicInst *II) {
+  // If the begincatch call is already the first instruction in the block,
+  // don't split.
+  Instruction *FirstNonPHI = BB->getFirstNonPHI();
+  if (II == FirstNonPHI)
+    return nullptr;
+
+  // If either operand is in the same basic block as the instruction and
+  // isn't used by another instruction before the begincatch call, include it
+  // in the split block.
+  auto *Op0 = dyn_cast<Instruction>(II->getOperand(0));
+  auto *Op1 = dyn_cast<Instruction>(II->getOperand(1));
+
+  Instruction *I = II->getPrevNode();
+  Instruction *LastI = II;
+
+  while (I == Op0 || I == Op1) {
+    // If the block begins with one of the operands and there are no other
+    // instructions between the operand and the begincatch call, don't split.
+    if (I == FirstNonPHI)
+      return nullptr;
+
+    LastI = I;
+    I = I->getPrevNode();
+  }
+
+  // If there is at least one instruction in the block before the begincatch
+  // call and its operands, split the block at either the begincatch or
+  // its operand.
+  return LastI;
+}
+
 /// Find all points where exceptional control rejoins normal control flow via
 /// llvm.eh.endcatch. Add them to the normal bb reachability worklist.
-static void findCXXEHReturnPoints(Function &F,
-                                  SetVector<BasicBlock *> &EHReturnBlocks) {
+void WinEHPrepare::findCXXEHReturnPoints(
+    Function &F, SetVector<BasicBlock *> &EHReturnBlocks) {
   for (auto BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) {
     BasicBlock *BB = BBI;
     for (Instruction &I : *BB) {
+      if (match(&I, m_Intrinsic<Intrinsic::eh_begincatch>())) {
+        Instruction *SplitPt = 
+            findBeginCatchSplitPoint(BB, cast<IntrinsicInst>(&I));
+        if (SplitPt) {
+          // Split the block before the llvm.eh.begincatch call to allow
+          // cleanup and catch code to be distinguished later.
+          // Do not update BBI because we still need to process the
+          // portion of the block that we are splitting off.
+          SplitBlock(BB, &I, DT);
+          break;
+        }
+      }
       if (match(&I, m_Intrinsic<Intrinsic::eh_endcatch>())) {
         // Split the block after the call to llvm.eh.endcatch if there is
         // anything other than an unconditional branch, or if the successor
@@ -408,7 +461,7 @@ static void findCXXEHReturnPoints(Function &F,
             isa<PHINode>(Br->getSuccessor(0)->begin())) {
           DEBUG(dbgs() << "splitting block " << BB->getName()
                        << " with llvm.eh.endcatch\n");
-          BBI = BB->splitBasicBlock(I.getNextNode(), "ehreturn");
+          BBI = SplitBlock(BB, I.getNextNode(), DT);
         }
         // The next BB is normal control flow.
         EHReturnBlocks.insert(BB->getTerminator()->getSuccessor(0));
@@ -429,8 +482,8 @@ static bool isCatchAllLandingPad(const BasicBlock *BB) {
 
 /// Find all points where exceptions control rejoins normal control flow via
 /// selector dispatch.
-static void findSEHEHReturnPoints(Function &F,
-                                  SetVector<BasicBlock *> &EHReturnBlocks) {
+void WinEHPrepare::findSEHEHReturnPoints(
+    Function &F, SetVector<BasicBlock *> &EHReturnBlocks) {
   for (auto BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) {
     BasicBlock *BB = BBI;
     // If the landingpad is a catch-all, treat the whole lpad as if it is
@@ -682,8 +735,7 @@ bool WinEHPrepare::prepareExceptionHandlers(
     // Split the block after the landingpad instruction so that it is just a
     // call to llvm.eh.actions followed by indirectbr.
     assert(!isa<PHINode>(LPadBB->begin()) && "lpad phi not removed");
-    LPadBB->splitBasicBlock(LPad->getNextNode(),
-                            LPadBB->getName() + ".prepsplit");
+    SplitBlock(LPadBB, LPad->getNextNode(), DT);
     // Erase the branch inserted by the split so we can insert indirectbr.
     LPadBB->getTerminator()->eraseFromParent();
 
@@ -1085,7 +1137,7 @@ void WinEHPrepare::addStubInvokeToHandlerIfNeeded(Function *Handler,
   else
     Term = Unreached;
   BasicBlock *OldRetBB = Term->getParent();
-  BasicBlock *NewRetBB = SplitBlock(OldRetBB, Term);
+  BasicBlock *NewRetBB = SplitBlock(OldRetBB, Term, DT);
   // SplitBlock adds an unconditional branch instruction at the end of the
   // parent block.  We want to replace that with an invoke call, so we can
   // erase it now.
@@ -1257,8 +1309,7 @@ void WinEHPrepare::processSEHCatchHandler(CatchHandler *CatchAction,
   } else {
     // This must be a catch-all. Split the block after the landingpad.
     assert(CatchAction->getSelector()->isNullValue() && "expected catch-all");
-    HandlerBB =
-        StartBB->splitBasicBlock(StartBB->getFirstInsertionPt(), "catch.all");
+    HandlerBB = SplitBlock(StartBB, StartBB->getFirstInsertionPt(), DT);
   }
   IRBuilder<> Builder(HandlerBB->getFirstInsertionPt());
   Function *EHCodeFn = Intrinsic::getDeclaration(
@@ -1718,14 +1769,6 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad,
         BB = NextBB;
       }
 
-      // For C++ EH, check if there is any interesting cleanup code before we
-      // begin the catch. This is important because cleanups cannot rethrow
-      // exceptions but code called from catches can. For SEH, it isn't
-      // important if some finally code before a catch-all is executed out of
-      // line or after recovering from the exception.
-      if (Personality == EHPersonality::MSVC_CXX)
-        findCleanupHandlers(Actions, BB, BB);
-
       // Add the catch handler to the action list.
       CatchHandler *Action = nullptr;
       if (CatchHandlerMap.count(BB) && CatchHandlerMap[BB] != nullptr) {
@@ -1733,11 +1776,29 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad,
         Action = CatchHandlerMap[BB];
         assert(Action->getSelector() == ExpectedSelector);
       } else {
-        // Since this is a catch-all handler, the selector won't actually appear
-        // in the code anywhere.  ExpectedSelector here is the constant null ptr
-        // that we got from the landing pad instruction.
-        Action = new CatchHandler(BB, ExpectedSelector, nullptr);
-        CatchHandlerMap[BB] = Action;
+        // We don't expect a selector dispatch, but there may be a call to
+        // llvm.eh.begincatch, which separates catch handling code from
+        // cleanup code in the same control flow.  This call looks for the
+        // begincatch intrinsic.
+        Action = findCatchHandler(BB, NextBB, VisitedBlocks);
+        if (Action) {
+          // For C++ EH, check if there is any interesting cleanup code before
+          // we begin the catch. This is important because cleanups cannot
+          // rethrow exceptions but code called from catches can. For SEH, it
+          // isn't important if some finally code before a catch-all is executed
+          // out of line or after recovering from the exception.
+          if (Personality == EHPersonality::MSVC_CXX)
+            findCleanupHandlers(Actions, BB, BB);
+        } else {
+          // If an action was not found, it means that the control flows
+          // directly into the catch-all handler and there is no cleanup code.
+          // That's an expected situation and we must create a catch action.
+          // Since this is a catch-all handler, the selector won't actually
+          // appear in the code anywhere.  ExpectedSelector here is the constant
+          // null ptr that we got from the landing pad instruction.
+          Action = new CatchHandler(BB, ExpectedSelector, nullptr);
+          CatchHandlerMap[BB] = Action;
+        }
       }
       Actions.insertCatchHandler(Action);
       DEBUG(dbgs() << "  Catch all handler at block " << BB->getName() << "\n");
@@ -2059,11 +2120,14 @@ void WinEHPrepare::findCleanupHandlers(LandingPadActions &Actions,
         // for finally calls in the normal successor block.
         BasicBlock *SuccBB = BB;
         if (FinallyCall.getInstruction() != BB->getTerminator() &&
-            FinallyCall.getInstruction()->getNextNode() != BB->getTerminator()) {
-          SuccBB = BB->splitBasicBlock(FinallyCall.getInstruction()->getNextNode());
+            FinallyCall.getInstruction()->getNextNode() !=
+                BB->getTerminator()) {
+          SuccBB =
+              SplitBlock(BB, FinallyCall.getInstruction()->getNextNode(), DT);
         } else {
           if (FinallyCall.isInvoke()) {
-            SuccBB = cast<InvokeInst>(FinallyCall.getInstruction())->getNormalDest();
+            SuccBB =
+                cast<InvokeInst>(FinallyCall.getInstruction())->getNormalDest();
           } else {
             SuccBB = BB->getUniqueSuccessor();
             assert(SuccBB && "splitOutlinedFinallyCalls didn't insert a branch");
index 8be0fbc4c3ab67743700622797efe97e8fb628e8..2de2cc67aaa7b2d0f7ed2a045e236d21dc2fc4e4 100644 (file)
@@ -55,18 +55,18 @@ resume:
 ; CHECK: invoke void @might_throw()
 ;
 ; CHECK: landingpad
-; CHECK: indirectbr i8* {{.*}}, [label %ehreturn]
+; CHECK: indirectbr i8* {{.*}}, [label %catchit.split]
 ;
-; CHECK: ehreturn:
+; CHECK: catchit.split:
 ; CHECK: load i32, i32* %val.lpad.reg2mem
 ; CHECK: br label %ret
 ;
 ; CHECK: ret:
-; CHECK: %rv = phi i32 [ {{.*}}, %entry ], [ {{.*}}, %ehreturn ]
+; CHECK: %rv = phi i32 [ {{.*}}, %entry ], [ {{.*}}, %catchit.split ]
 ; CHECK: ret i32
 
 ; CHECK-LABEL: define internal i8* @liveout_catch.catch(i8*, i8*)
 ; CHECK: %[[val:[^ ]*]] = load i32, i32*
 ; CHECK-NEXT: %[[val_lpad:[^ ]*]] = add i32 %[[val]], 1
 ; CHECK-NEXT: store i32 %[[val_lpad]], i32*
-; CHECK: ret i8* blockaddress(@liveout_catch, %ehreturn)
+; CHECK: ret i8* blockaddress(@liveout_catch, %catchit.split)
diff --git a/test/CodeGen/WinEH/cppeh-mixed-catch-and-cleanup.ll b/test/CodeGen/WinEH/cppeh-mixed-catch-and-cleanup.ll
new file mode 100644 (file)
index 0000000..52f6132
--- /dev/null
@@ -0,0 +1,106 @@
+; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s
+
+; This test is based on the following code:
+;
+; void test()
+; {
+;   try {
+;     Obj o;
+;     may_throw();
+;   } catch (...) {
+;   }
+; }
+;
+; The purpose of this test is to verify that we create separate catch and
+; cleanup handlers.  When compiling for the C++ 11 standard, this isn't
+; strictly necessary, since calling the destructor from the catch handler
+; would be logically equivalent to calling it from a cleanup handler.
+; However, if the -std=c++98 option is used, an exception in the cleanup
+; code should terminate the process (the MSVCRT runtime will do that) but
+; if the destructor is called from the catch handler, it wouldn't terminate
+; the process
+
+
+; ModuleID = 'cppeh-mixed-catch-and-cleanup.cpp'
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+%class.Obj = type { i8 }
+
+; This just verifies that the function was processed by WinEHPrepare.
+;
+; CHECK-LABEL: define void @"\01?test@@YAXXZ"()
+; CHECK: entry:
+; CHECK:   call void (...) @llvm.frameescape
+; CHECK: }
+
+; Function Attrs: nounwind uwtable
+define void @"\01?test@@YAXXZ"() #0 {
+entry:
+  %o = alloca %class.Obj, align 1
+  %exn.slot = alloca i8*
+  %ehselector.slot = alloca i32
+  invoke void @"\01?may_throw@@YAXXZ"()
+          to label %invoke.cont unwind label %lpad
+
+invoke.cont:                                      ; preds = %entry
+  call void @"\01??1Obj@@QEAA@XZ"(%class.Obj* %o) #3
+  br label %try.cont
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* null
+  %1 = extractvalue { i8*, i32 } %0, 0
+  store i8* %1, i8** %exn.slot
+  %2 = extractvalue { i8*, i32 } %0, 1
+  store i32 %2, i32* %ehselector.slot
+  call void @"\01??1Obj@@QEAA@XZ"(%class.Obj* %o) #3
+  %exn = load i8*, i8** %exn.slot
+  call void @llvm.eh.begincatch(i8* %exn, i8* null) #3
+  call void @llvm.eh.endcatch() #3
+  br label %try.cont
+
+try.cont:                                         ; preds = %catch, %invoke.cont
+  ret void
+}
+
+; Verify that a cleanup handler was created and that it calls ~Obj().
+; CHECK-LABEL: define internal void @"\01?test@@YAXXZ.cleanup"(i8*, i8*)
+; CHECK: entry:
+; CHECK:   @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0)
+; CHECK:   call void @"\01??1Obj@@QEAA@XZ"
+; CHECK:   ret void
+; CHECK: }
+
+; Verify that a catch handler was created and that it does not call ~Obj().
+; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*)
+; CHECK: entry:
+; CHECK-NOT:  call void @"\01??1Obj@@QEAA@XZ"
+; CHECK:   ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont)
+; CHECK: }
+
+
+
+declare void @"\01?may_throw@@YAXXZ"() #1
+
+declare i32 @__CxxFrameHandler3(...)
+
+; Function Attrs: nounwind
+declare void @"\01??1Obj@@QEAA@XZ"(%class.Obj*) #2
+
+; Function Attrs: nounwind
+declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3
+
+; Function Attrs: nounwind
+declare void @llvm.eh.endcatch() #3
+
+attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #3 = { nounwind }
+
+!llvm.module.flags = !{!0}
+!llvm.ident = !{!1}
+
+!0 = !{i32 1, !"PIC Level", i32 2}
+!1 = !{!"clang version 3.7.0 (trunk 235779) (llvm/trunk 235769)"}
index 0f8e805cc7c6762769f98e5f7f2590c25d17ba2b..dd99a092b2014b422f23c8652f988cdbf1dc5d31 100644 (file)
@@ -59,7 +59,7 @@ lpad:                                             ; preds = %entry
 ; CHECK:   landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
 ; CHECK-NEXT:           catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0
 ; CHECK-NEXT:           catch i8* null
-; CHECK-NEXT:   [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1")
+; CHECK-NEXT:   [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch")
 ; CHECK-NEXT:   indirectbr i8* [[RECOVER]], [label %try.cont4]
 
 lpad1:                                            ; preds = %invoke.cont
index ab6c9effbf477d0bd3715be3fefc6aaa599df1ad..c2a652b80990e37ffa60edd0ed300656914de296 100644 (file)
@@ -51,9 +51,9 @@ __try.cont:                                       ; preds = %__except, %invoke.c
 ; CHECK-LABEL: define void @seh_catch_all()
 ; CHECK: landingpad
 ; CHECK-NEXT: catch i8* null
-; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* blockaddress(@seh_catch_all, %catch.all))
+; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* blockaddress(@seh_catch_all, %lpad.split))
 ; CHECK-NEXT: indirectbr
 ;
-; CHECK: catch.all:
+; CHECK: lpad.split:
 ; CHECK-NOT: extractvalue
 ; CHECK: call i32 @puts
index a2a2139c270d0e0744c5e4feb0813291fd861a2a..aa9a6ca03b1ca38fed7c8596c12bc9973d2f29ff 100644 (file)
@@ -63,10 +63,10 @@ return:
 ; CHECK-LABEL: define i32 @catch_all()
 ; CHECK: landingpad { i8*, i32 }
 ; CHECK-NEXT: catch i8* null
-; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* blockaddress(@catch_all, %catch.all))
-; CHECK-NEXT: indirectbr {{.*}} [label %catch.all]
+; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* blockaddress(@catch_all, %lpad.split))
+; CHECK-NEXT: indirectbr {{.*}} [label %lpad.split]
 ;
-; CHECK: catch.all:
+; CHECK: lpad.split:
 ; CHECK: store i32 1, i32* %retval