r174146 - Destroy arrays and ARC fields when throwing out of ctors.

John McCall rjmccall at apple.com
Thu Jan 31 21:11:40 PST 2013


Author: rjmccall
Date: Thu Jan 31 23:11:40 2013
New Revision: 174146

URL: http://llvm.org/viewvc/llvm-project?rev=174146&view=rev
Log:
Destroy arrays and ARC fields when throwing out of ctors.

Previously we were only handling non-array fields of class type.

Testcases derived from a patch by WenHan Gu.

Modified:
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CGDecl.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/test/CodeGenCXX/exceptions.cpp
    cfe/trunk/test/CodeGenObjCXX/arc-exceptions.mm

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=174146&r1=174145&r2=174146&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Thu Jan 31 23:11:40 2013
@@ -532,21 +532,6 @@ static void EmitAggMemberInitializer(Cod
   CGF.EmitBlock(AfterFor, true);
 }
 
-namespace {
-  struct CallMemberDtor : EHScopeStack::Cleanup {
-    llvm::Value *V;
-    CXXDestructorDecl *Dtor;
-
-    CallMemberDtor(llvm::Value *V, CXXDestructorDecl *Dtor)
-      : V(V), Dtor(Dtor) {}
-
-    void Emit(CodeGenFunction &CGF, Flags flags) {
-      CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
-                                /*Delegating=*/false, V);
-    }
-  };
-}
-
 static void EmitMemberInitializer(CodeGenFunction &CGF,
                                   const CXXRecordDecl *ClassDecl,
                                   CXXCtorInitializer *MemberInit,
@@ -652,22 +637,13 @@ void CodeGenFunction::EmitInitializerFor
     
     EmitAggMemberInitializer(*this, LHS, Init, ArrayIndexVar, FieldType,
                              ArrayIndexes, 0);
-    
-    if (!CGM.getLangOpts().Exceptions)
-      return;
-
-    // FIXME: If we have an array of classes w/ non-trivial destructors, 
-    // we need to destroy in reverse order of construction along the exception
-    // path.
-    const RecordType *RT = FieldType->getAs<RecordType>();
-    if (!RT)
-      return;
-    
-    CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
-    if (!RD->hasTrivialDestructor())
-      EHStack.pushCleanup<CallMemberDtor>(EHCleanup, LHS.getAddress(),
-                                          RD->getDestructor());
   }
+
+  // Ensure that we destroy this object if an exception is thrown
+  // later in the constructor.
+  QualType::DestructionKind dtorKind = FieldType.isDestructedType();
+  if (needsEHCleanup(dtorKind))
+    pushEHDestroy(dtorKind, LHS.getAddress(), FieldType);
 }
 
 /// Checks whether the given constructor is a valid subject for the

Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=174146&r1=174145&r2=174146&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGDecl.cpp Thu Jan 31 23:11:40 2013
@@ -1242,7 +1242,18 @@ CodeGenFunction::getDestroyer(QualType::
   llvm_unreachable("Unknown DestructionKind");
 }
 
-/// pushDestroy - Push the standard destructor for the given type.
+/// pushEHDestroy - Push the standard destructor for the given type as
+/// an EH-only cleanup.
+void CodeGenFunction::pushEHDestroy(QualType::DestructionKind dtorKind,
+                                  llvm::Value *addr, QualType type) {
+  assert(dtorKind && "cannot push destructor for trivial type");
+  assert(needsEHCleanup(dtorKind));
+
+  pushDestroy(EHCleanup, addr, type, getDestroyer(dtorKind), true);
+}
+
+/// pushDestroy - Push the standard destructor for the given type as
+/// at least a normal cleanup.
 void CodeGenFunction::pushDestroy(QualType::DestructionKind dtorKind,
                                   llvm::Value *addr, QualType type) {
   assert(dtorKind && "cannot push destructor for trivial type");

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=174146&r1=174145&r2=174146&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Thu Jan 31 23:11:40 2013
@@ -1282,6 +1282,8 @@ public:
 
   void pushDestroy(QualType::DestructionKind dtorKind,
                    llvm::Value *addr, QualType type);
+  void pushEHDestroy(QualType::DestructionKind dtorKind,
+                     llvm::Value *addr, QualType type);
   void pushDestroy(CleanupKind kind, llvm::Value *addr, QualType type,
                    Destroyer *destroyer, bool useEHCleanupForArray);
   void emitDestroy(llvm::Value *addr, QualType type, Destroyer *destroyer,

Modified: cfe/trunk/test/CodeGenCXX/exceptions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/exceptions.cpp?rev=174146&r1=174145&r2=174146&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/exceptions.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/exceptions.cpp Thu Jan 31 23:11:40 2013
@@ -451,3 +451,70 @@ namespace test10 {
   // CHECK:      invoke void @__cxa_rethrow()
   // CHECK:      unreachable
 }
+
+// Ensure that an exception in a constructor destroys
+// already-constructed array members.  PR14514
+namespace test11 {
+  struct A {
+    A();
+    ~A() {}
+  };
+
+  struct C {
+    A single;
+    A array[2][3];
+
+    C();
+  };
+
+  C::C() {
+    throw 0;
+  }
+  // CHECK:    define void @_ZN6test111CC2Ev(
+  // CHECK:      [[THIS:%.*]] = load [[C:%.*]]** {{%.*}}
+  //   Construct single.
+  // CHECK-NEXT: [[SINGLE:%.*]] = getelementptr inbounds [[C]]* [[THIS]], i32 0, i32 0
+  // CHECK-NEXT: call void @_ZN6test111AC1Ev([[A:%.*]]* [[SINGLE]])
+  //   Construct array.
+  // CHECK-NEXT: [[ARRAY:%.*]] = getelementptr inbounds [[C]]* [[THIS]], i32 0, i32 1
+  // CHECK-NEXT: [[ARRAYBEGIN:%.*]] = getelementptr inbounds [2 x [3 x [[A]]]]* [[ARRAY]], i32 0, i32 0, i32 0
+  // CHECK-NEXT: [[ARRAYEND:%.*]] = getelementptr inbounds [[A]]* [[ARRAYBEGIN]], i64 6
+  // CHECK-NEXT: br label
+  // CHECK:      [[CUR:%.*]] = phi [[A]]* [ [[ARRAYBEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ]
+  // CHECK-NEXT: invoke void @_ZN6test111AC1Ev([[A:%.*]]* [[CUR]])
+  // CHECK:      [[NEXT]] = getelementptr inbounds [[A]]* [[CUR]], i64 1
+  // CHECK-NEXT: [[DONE:%.*]] = icmp eq [[A]]* [[NEXT]], [[ARRAYEND]]
+  // CHECK-NEXT: br i1 [[DONE]],
+  //   throw 0;
+  // CHECK:      invoke void @__cxa_throw(
+  //   Landing pad 1, from constructor in array-initialization loop:
+  // CHECK:      landingpad
+  //     - First, destroy already-constructed bits of array.
+  // CHECK:      [[EMPTY:%.*]] = icmp eq [[A]]* [[ARRAYBEGIN]], [[CUR]]
+  // CHECK-NEXT: br i1 [[EMPTY]]
+  // CHECK:      [[AFTER:%.*]] = phi [[A]]* [ [[CUR]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ]
+  // CHECK-NEXT: [[ELT]] = getelementptr inbounds [[A]]* [[AFTER]], i64 -1
+  // CHECK-NEXT: invoke void @_ZN6test111AD1Ev([[A]]* [[ELT]])
+  // CHECK:      [[DONE:%.*]] = icmp eq [[A]]* [[ELT]], [[ARRAYBEGIN]]
+  // CHECK-NEXT: br i1 [[DONE]],
+  //     - Next, chain to cleanup for single.
+  // CHECK:      br label
+  //   Landing pad 2, from throw site.
+  // CHECK:      landingpad
+  //     - First, destroy all of array.
+  // CHECK:      [[ARRAYBEGIN:%.*]] = getelementptr inbounds [2 x [3 x [[A]]]]* [[ARRAY]], i32 0, i32 0, i32 0
+  // CHECK-NEXT: [[ARRAYEND:%.*]] = getelementptr inbounds [[A]]* [[ARRAYBEGIN]], i64 6
+  // CHECK-NEXT: br label
+  // CHECK:      [[AFTER:%.*]] = phi [[A]]* [ [[ARRAYEND]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ]
+  // CHECK-NEXT: [[ELT]] = getelementptr inbounds [[A]]* [[AFTER]], i64 -1
+  // CHECK-NEXT: invoke void @_ZN6test111AD1Ev([[A]]* [[ELT]])
+  // CHECK:      [[DONE:%.*]] = icmp eq [[A]]* [[ELT]], [[ARRAYBEGIN]]
+  // CHECK-NEXT: br i1 [[DONE]],
+  //     - Next, chain to cleanup for single.
+  // CHECK:      br label
+  //   Finally, the cleanup for single.
+  // CHECK:      invoke void @_ZN6test111AD1Ev([[A]]* [[SINGLE]])
+  // CHECK:      br label
+  // CHECK:      resume
+  //   (After this is a terminate landingpad.)
+}

Modified: cfe/trunk/test/CodeGenObjCXX/arc-exceptions.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjCXX/arc-exceptions.mm?rev=174146&r1=174145&r2=174146&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjCXX/arc-exceptions.mm (original)
+++ cfe/trunk/test/CodeGenObjCXX/arc-exceptions.mm Thu Jan 31 23:11:40 2013
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fcxx-exceptions -fobjc-runtime-has-weak -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fcxx-exceptions -fobjc-runtime-has-weak -o - -fobjc-arc-exceptions %s | FileCheck %s
 
 @class Ety;
 
@@ -81,3 +81,42 @@ void test3(void) {
 // CHECK-NEXT: [[T0:%.*]] = bitcast [[ETY]]** [[E]] to i8**
 // CHECK-NEXT: call void @objc_destroyWeak(i8** [[T0]]) nounwind
 // CHECK-NEXT: call void @__cxa_end_catch() nounwind
+
+namespace test4 {
+  struct A {
+    id single;
+    id array[2][3];
+
+    A();
+  };
+
+  A::A() {
+    throw 0;
+  }
+  // CHECK:    define void @_ZN5test41AC2Ev(
+  // CHECK:      [[THIS:%.*]] = load [[A:%.*]]** {{%.*}}
+  //   Construct single.
+  // CHECK-NEXT: [[SINGLE:%.*]] = getelementptr inbounds [[A]]* [[THIS]], i32 0, i32 0
+  // CHECK-NEXT: store i8* null, i8** [[SINGLE]], align 8
+  //   Construct array.
+  // CHECK-NEXT: [[ARRAY:%.*]] = getelementptr inbounds [[A]]* [[THIS]], i32 0, i32 1
+  // CHECK-NEXT: [[T0:%.*]] = bitcast [2 x [3 x i8*]]* [[ARRAY]] to i8*
+  // CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 48, i32 8, i1 false)
+  //   throw 0;
+  // CHECK:      invoke void @__cxa_throw(
+  //   Landing pad from throw site:
+  // CHECK:      landingpad
+  //     - First, destroy all of array.
+  // CHECK:      [[ARRAYBEGIN:%.*]] = getelementptr inbounds [2 x [3 x i8*]]* [[ARRAY]], i32 0, i32 0, i32 0
+  // CHECK-NEXT: [[ARRAYEND:%.*]] = getelementptr inbounds i8** [[ARRAYBEGIN]], i64 6
+  // CHECK-NEXT: br label
+  // CHECK:      [[AFTER:%.*]] = phi i8** [ [[ARRAYEND]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ]
+  // CHECK-NEXT: [[ELT]] = getelementptr inbounds i8** [[AFTER]], i64 -1
+  // CHECK-NEXT: call void @objc_storeStrong(i8** [[ELT]], i8* null) nounwind
+  // CHECK-NEXT: [[DONE:%.*]] = icmp eq i8** [[ELT]], [[ARRAYBEGIN]]
+  // CHECK-NEXT: br i1 [[DONE]],
+  //     - Next, destroy single.
+  // CHECK:      call void @objc_storeStrong(i8** [[SINGLE]], i8* null) nounwind
+  // CHECK:      br label
+  // CHECK:      resume
+}





More information about the cfe-commits mailing list