]> granicus.if.org Git - clang/commitdiff
Allow for the possibility that __cxa_end_catch might throw for a catch-all block
authorJohn McCall <rjmccall@apple.com>
Tue, 13 Jul 2010 22:12:14 +0000 (22:12 +0000)
committerJohn McCall <rjmccall@apple.com>
Tue, 13 Jul 2010 22:12:14 +0000 (22:12 +0000)
or a catch of a record type by value or reference.  Also convert this to a
lazy cleanup.

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

lib/CodeGen/CGException.cpp
lib/CodeGen/CGObjCMac.cpp
lib/CodeGen/CodeGenFunction.h
test/CodeGenCXX/eh.cpp

index d3949baf3ae823751c3c28840c504a407c3730bb..5e91adc669e6c4bc0187f413a7b216aae2c80daa 100644 (file)
@@ -1020,18 +1020,45 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() {
   return LP;
 }
 
+namespace {
+  /// A cleanup to call __cxa_end_catch.  In many cases, the caught
+  /// exception type lets us state definitively that the thrown exception
+  /// type does not have a destructor.  In particular:
+  ///   - Catch-alls tell us nothing, so we have to conservatively
+  ///     assume that the thrown exception might have a destructor.
+  ///   - Catches by reference behave according to their base types.
+  ///   - Catches of non-record types will only trigger for exceptions
+  ///     of non-record types, which never have destructors.
+  ///   - Catches of record types can trigger for arbitrary subclasses
+  ///     of the caught type, so we have to assume the actual thrown
+  ///     exception type might have a throwing destructor, even if the
+  ///     caught type's destructor is trivial or nothrow.
+  struct CallEndCatch : EHScopeStack::LazyCleanup {
+    CallEndCatch(bool MightThrow) : MightThrow(MightThrow) {}
+    bool MightThrow;
+
+    void Emit(CodeGenFunction &CGF, bool IsForEH) {
+      if (!MightThrow) {
+        CGF.Builder.CreateCall(getEndCatchFn(CGF))->setDoesNotThrow();
+        return;
+      }
+
+      CGF.EmitCallOrInvoke(getEndCatchFn(CGF), 0, 0);
+    }
+  };
+}
+
 /// Emits a call to __cxa_begin_catch and enters a cleanup to call
 /// __cxa_end_catch.
-static llvm::Value *CallBeginCatch(CodeGenFunction &CGF, llvm::Value *Exn) {
+///
+/// \param EndMightThrow - true if __cxa_end_catch might throw
+static llvm::Value *CallBeginCatch(CodeGenFunction &CGF,
+                                   llvm::Value *Exn,
+                                   bool EndMightThrow) {
   llvm::CallInst *Call = CGF.Builder.CreateCall(getBeginCatchFn(CGF), Exn);
   Call->setDoesNotThrow();
 
-  {
-    CodeGenFunction::CleanupBlock EndCatchCleanup(CGF, NormalAndEHCleanup);
-
-    // __cxa_end_catch never throws, so this can just be a call.
-    CGF.Builder.CreateCall(getEndCatchFn(CGF))->setDoesNotThrow();
-  }
+  CGF.EHStack.pushLazyCleanup<CallEndCatch>(NormalAndEHCleanup, EndMightThrow);
 
   return Call;
 }
@@ -1051,8 +1078,11 @@ static void InitCatchParam(CodeGenFunction &CGF,
   // If we're catching by reference, we can just cast the object
   // pointer to the appropriate pointer.
   if (isa<ReferenceType>(CatchType)) {
+    bool EndCatchMightThrow = cast<ReferenceType>(CatchType)->getPointeeType()
+      ->isRecordType();
+
     // __cxa_begin_catch returns the adjusted object pointer.
-    llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn);
+    llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, EndCatchMightThrow);
     llvm::Value *ExnCast =
       CGF.Builder.CreateBitCast(AdjustedExn, LLVMCatchTy, "exn.byref");
     CGF.Builder.CreateStore(ExnCast, ParamAddr);
@@ -1063,7 +1093,7 @@ static void InitCatchParam(CodeGenFunction &CGF,
   bool IsComplex = false;
   if (!CGF.hasAggregateLLVMType(CatchType) ||
       (IsComplex = CatchType->isAnyComplexType())) {
-    llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn);
+    llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, false);
     
     // If the catch type is a pointer type, __cxa_begin_catch returns
     // the pointer by value.
@@ -1098,7 +1128,7 @@ static void InitCatchParam(CodeGenFunction &CGF,
   const llvm::Type *PtrTy = LLVMCatchTy->getPointerTo(0); // addrspace 0 ok
 
   if (RD->hasTrivialCopyConstructor()) {
-    llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn);
+    llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, true);
     llvm::Value *Cast = CGF.Builder.CreateBitCast(AdjustedExn, PtrTy);
     CGF.EmitAggregateCopy(ParamAddr, Cast, CatchType);
     return;
@@ -1131,7 +1161,7 @@ static void InitCatchParam(CodeGenFunction &CGF,
   CGF.EHStack.popTerminate();
 
   // Finally we can call __cxa_begin_catch.
-  CallBeginCatch(CGF, Exn);
+  CallBeginCatch(CGF, Exn, true);
 }
 
 /// Begins a catch statement by initializing the catch variable and
@@ -1164,7 +1194,7 @@ static void BeginCatch(CodeGenFunction &CGF,
   VarDecl *CatchParam = S->getExceptionDecl();
   if (!CatchParam) {
     llvm::Value *Exn = CGF.Builder.CreateLoad(CGF.getExceptionSlot(), "exn");
-    CallBeginCatch(CGF, Exn);
+    CallBeginCatch(CGF, Exn, true);
     return;
   }
 
@@ -1298,7 +1328,7 @@ CodeGenFunction::EnterFinallyBlock(const Stmt *Body,
         Builder.CreateLoad(ForEHVar, "finally.endcatch");
       Builder.CreateCondBr(ShouldEndCatch, EndCatchBB, CleanupContBB);
       EmitBlock(EndCatchBB);
-      Builder.CreateCall(EndCatchFn)->setDoesNotThrow();
+      EmitCallOrInvoke(EndCatchFn, 0, 0); // catch-all, so might throw
       EmitBlock(CleanupContBB);
     }
 
index 72935373027241de3427405ec34f03f85b140a3d..2992867cbc1273fc08e638850e8943d5f1b47b4b 100644 (file)
@@ -32,6 +32,7 @@
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/CallSite.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Target/TargetData.h"
 #include <cstdio>
@@ -5712,6 +5713,22 @@ namespace {
     llvm::BasicBlock *Block;
     llvm::Value *TypeInfo;
   };
+
+  struct CallObjCEndCatch : EHScopeStack::LazyCleanup {
+    CallObjCEndCatch(bool MightThrow, llvm::Value *Fn) :
+      MightThrow(MightThrow), Fn(Fn) {}
+    bool MightThrow;
+    llvm::Value *Fn;
+
+    void Emit(CodeGenFunction &CGF, bool IsForEH) {
+      if (!MightThrow) {
+        CGF.Builder.CreateCall(Fn)->setDoesNotThrow();
+        return;
+      }
+
+      CGF.EmitCallOrInvoke(Fn, 0, 0);
+    }
+  };
 }
 
 void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
@@ -5801,13 +5818,10 @@ void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
     Exn->setDoesNotThrow();
 
     // Add a cleanup to leave the catch.
-    {
-      CodeGenFunction::CleanupBlock EndCatchBlock(CGF, NormalAndEHCleanup);
-
-      // __objc_end_catch never throws.
-      CGF.Builder.CreateCall(ObjCTypes.getObjCEndCatchFn())
-        ->setDoesNotThrow();
-    }
+    bool EndCatchMightThrow = (Handler.Variable == 0);
+    CGF.EHStack.pushLazyCleanup<CallObjCEndCatch>(NormalAndEHCleanup,
+                                                  EndCatchMightThrow,
+                                                  ObjCTypes.getObjCEndCatchFn());
 
     // Bind the catch parameter if it exists.
     if (const VarDecl *CatchParam = Handler.Variable) {
index ce3453ae5e803bc0f83c9879c431ee705bf8a4fc..90a5421c69558a32007b6afa79b0407b6b6b4e1b 100644 (file)
@@ -206,6 +206,22 @@ public:
 
   // Variadic templates would make this not terrible.
 
+  /// Push a lazily-created cleanup on the stack.
+  template <class T>
+  void pushLazyCleanup(CleanupKind Kind) {
+    void *Buffer = pushLazyCleanup(Kind, sizeof(T));
+    LazyCleanup *Obj = new(Buffer) T();
+    (void) Obj;
+  }
+
+  /// Push a lazily-created cleanup on the stack.
+  template <class T, class A0>
+  void pushLazyCleanup(CleanupKind Kind, A0 a0) {
+    void *Buffer = pushLazyCleanup(Kind, sizeof(T));
+    LazyCleanup *Obj = new(Buffer) T(a0);
+    (void) Obj;
+  }
+
   /// Push a lazily-created cleanup on the stack.
   template <class T, class A0, class A1>
   void pushLazyCleanup(CleanupKind Kind, A0 a0, A1 a1) {
index f8d7ef69e009386b55a56a5e418cd3b573c45739..6d79c3e17b51ac1c6c5c7146fa36b9ee26ac211b 100644 (file)
@@ -181,11 +181,7 @@ namespace test8 {
       // CHECK-NEXT: invoke void @_ZN5test81AC1ERKS0_(
       // CHECK:      call i8* @__cxa_begin_catch
       // CHECK-NEXT: invoke void @_ZN5test81AD1Ev(
-
       // CHECK:      call void @__cxa_end_catch()
-      // CHECK-NEXT: load
-      // CHECK-NEXT: switch
-
       // CHECK:      ret void
     }
   }
@@ -217,3 +213,39 @@ namespace test9 {
   // CHECK:      call i8* @llvm.eh.exception
   // CHECK:      call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* {{.*}}, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* bitcast (i8** @_ZTIi to i8*), i8* null)
 }
+
+// __cxa_end_catch can throw for some kinds of caught exceptions.
+namespace test10 {
+  void opaque();
+
+  struct A { ~A(); };
+  struct B { int x; };
+
+  // CHECK: define void @_ZN6test103fooEv()
+  void foo() {
+    A a; // force a cleanup context
+
+    try {
+    // CHECK:      invoke void @_ZN6test106opaqueEv()
+      opaque();
+    } catch (int i) {
+    // CHECK:      call i8* @__cxa_begin_catch
+    // CHECK-NEXT: bitcast
+    // CHECK-NEXT: load i32*
+    // CHECK-NEXT: store i32
+    // CHECK-NEXT: call void @__cxa_end_catch() nounwind
+    } catch (B a) {
+    // CHECK:      call i8* @__cxa_begin_catch
+    // CHECK-NEXT: bitcast
+    // CHECK-NEXT: bitcast
+    // CHECK-NEXT: bitcast
+    // CHECK-NEXT: call void @llvm.memcpy
+    // CHECK-NEXT: invoke void @__cxa_end_catch()
+    } catch (...) {
+    // CHECK:      call i8* @__cxa_begin_catch
+    // CHECK-NEXT: invoke void @__cxa_end_catch()
+    }
+
+    // CHECK: call void @_ZN6test101AD1Ev(
+  }
+}