]> granicus.if.org Git - clang/commitdiff
Ensure that destructors are called for NRVO'd objects when the
authorDouglas Gregor <dgregor@apple.com>
Mon, 17 May 2010 15:52:46 +0000 (15:52 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 17 May 2010 15:52:46 +0000 (15:52 +0000)
function does not return. Thanks to Eli for pointing out this corner
case.

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

lib/CodeGen/CGDecl.cpp
lib/CodeGen/CGStmt.cpp
lib/CodeGen/CodeGenFunction.h
test/CodeGenCXX/nrvo.cpp

index ae05fc5c8467c8652b63e97e91fec8849b8a92f0..611ebed5a3d1187da85027882c66e982dd97db7f 100644 (file)
@@ -399,6 +399,7 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
   bool IsSimpleConstantInitializer = false;
 
   bool NRVO = false;
+  llvm::Value *NRVOFlag = 0;
   llvm::Value *DeclPtr;
   if (Ty->isConstantSizeType()) {
     if (!Target.useGlobalsForAutomaticVariables()) {
@@ -430,6 +431,21 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
         // return slot, so that we can elide the copy when returning this
         // variable (C++0x [class.copy]p34).
         DeclPtr = ReturnValue;
+        
+        if (const RecordType *RecordTy = Ty->getAs<RecordType>()) {
+          if (!cast<CXXRecordDecl>(RecordTy->getDecl())->hasTrivialDestructor()) {
+            // Create a flag that is used to indicate when the NRVO was applied
+            // to this variable. Set it to zero to indicate that NRVO was not 
+            // applied.
+            const llvm::Type *BoolTy = llvm::Type::getInt1Ty(VMContext);
+            llvm::Value *Zero = llvm::ConstantInt::get(BoolTy, 0);
+            NRVOFlag = CreateTempAlloca(BoolTy, "nrvo");            
+            Builder.CreateStore(Zero, NRVOFlag);
+            
+            // Record the NRVO flag for this variable.
+            NRVOFlags[&D] = NRVOFlag;
+          }
+        }
       } else {
         if (isByRef)
           LTy = BuildByRefType(&D);
@@ -660,7 +676,8 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
   if (const RecordType *RT = DtorTy->getAs<RecordType>())
     if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) {      
       if (!ClassDecl->hasTrivialDestructor()) {
-        // Note: We suppress the destructor call when this is an NRVO variable.
+        // Note: We suppress the destructor call when the corresponding NRVO
+        // flag has been set.
         llvm::Value *Loc = DeclPtr;
         if (isByRef)
           Loc = Builder.CreateStructGEP(DeclPtr, getByRefValueLLVMField(&D), 
@@ -693,10 +710,21 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
             EmitCXXAggrDestructorCall(D, Array, BaseAddrPtr);
           }
         } else {
-          if (!NRVO) {
+          {
+            // Normal destruction. 
+            DelayedCleanupBlock Scope(*this);
+
+            if (NRVO) {
+              // If we exited via NRVO, we skip the destructor call.
+              llvm::BasicBlock *NoNRVO = createBasicBlock("nrvo.unused");
+              Builder.CreateCondBr(Builder.CreateLoad(NRVOFlag, "nrvo.val"),
+                                   Scope.getCleanupExitBlock(),
+                                   NoNRVO);
+              EmitBlock(NoNRVO);
+            }
+            
             // We don't call the destructor along the normal edge if we're
             // applying the NRVO.
-            DelayedCleanupBlock Scope(*this);
             EmitCXXDestructorCall(D, Dtor_Complete, /*ForVirtualBase=*/false,
                                   Loc);
 
index f51a899e7ffacfe1a1d69ce6962657bd36e86245..496fd2fe1fbad0b8127ad54c39344985506badb4 100644 (file)
@@ -612,6 +612,14 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
     // Apply the named return value optimization for this return statement,
     // which means doing nothing: the appropriate result has already been
     // constructed into the NRVO variable.
+    
+    // If there is an NRVO flag for this variable, set it to 1 into indicate
+    // that the cleanup code should not destroy the variable.
+    if (llvm::Value *NRVOFlag = NRVOFlags[S.getNRVOCandidate()]) {
+      const llvm::Type *BoolTy = llvm::Type::getInt1Ty(VMContext);
+      llvm::Value *One = llvm::ConstantInt::get(BoolTy, 1);
+      Builder.CreateStore(One, NRVOFlag);
+    }
   } else if (!ReturnValue) {
     // Make sure not to return anything, but evaluate the expression
     // for side effects.
index 4f85878f0c01a9f0b6035a068ced1ef99a706cd1..e3e7472317cc284b599d9d2b4694a1433a214edf 100644 (file)
@@ -107,6 +107,11 @@ public:
 
   bool Exceptions;
   bool CatchUndefined;
+  
+  /// \brief A mapping from NRVO variables to the flags used to indicate
+  /// when the NRVO has been applied to this variable.
+  llvm::DenseMap<const VarDecl *, llvm::Value *> NRVOFlags;
+  
 public:
   /// ObjCEHValueStack - Stack of Objective-C exception values, used for
   /// rethrows.
index 195b2b81fa0c47c611ae2662eb61cf20cd8f603d..9ee553673f18ea09897004cd2fc39ae3ca573b4d 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -O1 -o - %s | FileCheck %s
 // RUN: %clang_cc1 -emit-llvm -fexceptions -o - %s | FileCheck --check-prefix=CHECK-EH %s
 
 // Test code generation for the named return value optimization.
@@ -15,9 +15,12 @@ X test0() {
   X x;
   // CHECK-NOT: call void @_ZN1XD1Ev
   // CHECK: ret void
+  // CHECK-EH: br label
+  // CHECK-EH: call void @_ZN1XD1Ev
+  // CHECK-EH: br label
+  // CHECK-EH: invoke void @_ZN1XD1Ev
   // CHECK-EH: ret void
   return x;
-  // CHECK-EH: invoke void @_ZN1XD1Ev
 }
 
 // CHECK: define void @_Z5test1b(
@@ -63,3 +66,19 @@ X test3(bool B) {
   X x;
   return x;
 }
+
+extern "C" void exit(int) throw();
+
+// CHECK: define void @_Z5test4b
+X test4(bool B) {
+  {
+    // CHECK: tail call void @_ZN1XC1Ev
+    X x;
+    // CHECK: br i1
+    if (B)
+      return x;
+  }
+  // CHECK: tail call void @_ZN1XD1Ev
+  // CHECK: tail call void @exit(i32 1)
+  exit(1);
+}