r333500 - Update NRVO logic to support early return

Taiju Tsuiki via cfe-commits cfe-commits at lists.llvm.org
Tue May 29 20:53:17 PDT 2018


Author: tzik
Date: Tue May 29 20:53:16 2018
New Revision: 333500

URL: http://llvm.org/viewvc/llvm-project?rev=333500&view=rev
Log:
Update NRVO logic to support early return

Summary:
The previous implementation misses an opportunity to apply NRVO (Named Return Value
Optimization) below. That discourages user to write early return code.

```
struct Foo {};

Foo f(bool b) {
  if (b)
    return Foo();
  Foo oo;
  return oo;
}
```
That is, we can/should apply RVO for a local variable if:
 * It's directly returned by at least one return statement.
 * And, all reachable return statements in its scope returns the variable directly.
While, the previous implementation disables the RVO in a scope if there are multiple return
statements that refers different variables.

On the new algorithm, local variables are in NRVO_Candidate state at first, and a return
statement changes it to NRVO_Disabled for all visible variables but the return statement refers.
Then, at the end of the function AST traversal, NRVO is enabled for variables in NRVO_Candidate
state and refers from at least one return statement.

Reviewers: rsmith

Reviewed By: rsmith

Subscribers: xbolva00, Quuxplusone, arthur.j.odwyer, cfe-commits

Differential Revision: https://reviews.llvm.org/D47067

Added:
    cfe/trunk/test/CodeGenCXX/nrvo-noopt.cpp
    cfe/trunk/test/SemaCXX/nrvo-ast.cpp
Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/Sema/Scope.h
    cfe/trunk/lib/Sema/Scope.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaStmt.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
    cfe/trunk/test/CodeGenCXX/nrvo.cpp

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Tue May 29 20:53:16 2018
@@ -879,6 +879,12 @@ protected:
     DAK_Normal
   };
 
+  enum NRVOMode {
+    NRVO_Candidate,
+    NRVO_Disabled,
+    NRVO_Enabled,
+  };
+
   class ParmVarDeclBitfields {
     friend class ASTDeclReader;
     friend class ParmVarDecl;
@@ -931,7 +937,7 @@ protected:
     /// Whether this local variable could be allocated in the return
     /// slot of its function, enabling the named return value optimization
     /// (NRVO).
-    unsigned NRVOVariable : 1;
+    unsigned NRVOMode : 2;
 
     /// Whether this variable is the for-range-declaration in a C++0x
     /// for-range statement.
@@ -1319,12 +1325,20 @@ public:
   /// return slot when returning from the function. Within the function body,
   /// each return that returns the NRVO object will have this variable as its
   /// NRVO candidate.
+  NRVOMode getNRVOMode() const {
+    if (isa<ParmVarDecl>(this))
+      return NRVO_Disabled;
+    return static_cast<NRVOMode>(NonParmVarDeclBits.NRVOMode);
+  }
+  bool isNRVOCandidate() const {
+    return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOMode == NRVO_Candidate;
+  }
   bool isNRVOVariable() const {
-    return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOVariable;
+    return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOMode == NRVO_Enabled;
   }
   void setNRVOVariable(bool NRVO) {
     assert(!isa<ParmVarDecl>(this));
-    NonParmVarDeclBits.NRVOVariable = NRVO;
+    NonParmVarDeclBits.NRVOMode = NRVO ? NRVO_Enabled : NRVO_Disabled;
   }
 
   /// Determine whether this variable is the for-range-declaration in

Modified: cfe/trunk/include/clang/Sema/Scope.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Scope.h?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Scope.h (original)
+++ cfe/trunk/include/clang/Sema/Scope.h Tue May 29 20:53:16 2018
@@ -201,10 +201,6 @@ private:
   /// Used to determine if errors occurred in this scope.
   DiagnosticErrorTrap ErrorTrap;
 
-  /// A lattice consisting of undefined, a single NRVO candidate variable in
-  /// this scope, or over-defined. The bit is true when over-defined.
-  llvm::PointerIntPair<VarDecl *, 1, bool> NRVO;
-
   void setFlags(Scope *Parent, unsigned F);
 
 public:
@@ -466,23 +462,7 @@ public:
                                   UsingDirectives.end());
   }
 
-  void addNRVOCandidate(VarDecl *VD) {
-    if (NRVO.getInt())
-      return;
-    if (NRVO.getPointer() == nullptr) {
-      NRVO.setPointer(VD);
-      return;
-    }
-    if (NRVO.getPointer() != VD)
-      setNoNRVO();
-  }
-
-  void setNoNRVO() {
-    NRVO.setInt(true);
-    NRVO.setPointer(nullptr);
-  }
-
-  void mergeNRVOIntoParent();
+  void setNRVOCandidate(VarDecl *Candidate);
 
   /// Init - This is used by the parser to implement scope caching.
   void Init(Scope *parent, unsigned flags);

Modified: cfe/trunk/lib/Sema/Scope.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Scope.cpp?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Scope.cpp (original)
+++ cfe/trunk/lib/Sema/Scope.cpp Tue May 29 20:53:16 2018
@@ -92,7 +92,6 @@ void Scope::Init(Scope *parent, unsigned
   UsingDirectives.clear();
   Entity = nullptr;
   ErrorTrap.reset();
-  NRVO.setPointerAndInt(nullptr, 0);
 }
 
 bool Scope::containedInPrototypeScope() const {
@@ -119,19 +118,15 @@ void Scope::AddFlags(unsigned FlagsToSet
   Flags |= FlagsToSet;
 }
 
-void Scope::mergeNRVOIntoParent() {
-  if (VarDecl *Candidate = NRVO.getPointer()) {
-    if (isDeclScope(Candidate))
-      Candidate->setNRVOVariable(true);
+void Scope::setNRVOCandidate(VarDecl *Candidate) {
+  for (Decl *D : DeclsInScope) {
+    VarDecl *VD = dyn_cast<VarDecl>(D);
+    if (VD && VD != Candidate && VD->isNRVOCandidate())
+      VD->setNRVOVariable(false);
   }
 
-  if (getEntity())
-    return;
-
-  if (NRVO.getInt())
-    getParent()->setNoNRVO();
-  else if (NRVO.getPointer())
-    getParent()->addNRVOCandidate(NRVO.getPointer());
+  if (Scope *parent = getParent())
+    parent->setNRVOCandidate(Candidate);
 }
 
 LLVM_DUMP_METHOD void Scope::dump() const { dumpImpl(llvm::errs()); }
@@ -191,9 +186,4 @@ void Scope::dumpImpl(raw_ostream &OS) co
   OS << "MSCurManglingNumber: " << getMSCurManglingNumber() << '\n';
   if (const DeclContext *DC = getEntity())
     OS << "Entity : (clang::DeclContext*)" << DC << '\n';
-
-  if (NRVO.getInt())
-    OS << "NRVO not allowed\n";
-  else if (NRVO.getPointer())
-    OS << "NRVO candidate : (clang::VarDecl*)" << NRVO.getPointer() << '\n';
 }

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue May 29 20:53:16 2018
@@ -1798,8 +1798,6 @@ static void CheckPoppedLabel(LabelDecl *
 }
 
 void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
-  S->mergeNRVOIntoParent();
-
   if (S->decl_empty()) return;
   assert((S->getFlags() & (Scope::DeclScope | Scope::TemplateParamScope)) &&
          "Scope shouldn't contain decls!");
@@ -12611,21 +12609,24 @@ Decl *Sema::ActOnStartOfFunctionDef(Scop
 /// optimization.
 ///
 /// Each of the variables that is subject to the named return value
-/// optimization will be marked as NRVO variables in the AST, and any
+/// optimization will be marked as NRVO variable candidates in the AST, and any
 /// return statement that has a marked NRVO variable as its NRVO candidate can
 /// use the named return value optimization.
 ///
-/// This function applies a very simplistic algorithm for NRVO: if every return
-/// statement in the scope of a variable has the same NRVO candidate, that
-/// candidate is an NRVO variable.
+/// This function applies a very simplistic algorithm for NRVO: if every
+/// reachable return statement in the scope of a variable has the same NRVO
+/// candidate, that candidate is an NRVO variable.
 void Sema::computeNRVO(Stmt *Body, FunctionScopeInfo *Scope) {
-  ReturnStmt **Returns = Scope->Returns.data();
+  for (ReturnStmt *Return : Scope->Returns) {
+    const VarDecl *Candidate = Return->getNRVOCandidate();
+    if (!Candidate)
+      continue;
 
-  for (unsigned I = 0, E = Scope->Returns.size(); I != E; ++I) {
-    if (const VarDecl *NRVOCandidate = Returns[I]->getNRVOCandidate()) {
-      if (!NRVOCandidate->isNRVOVariable())
-        Returns[I]->setNRVOCandidate(nullptr);
-    }
+    if (Candidate->isNRVOCandidate())
+      const_cast<VarDecl*>(Candidate)->setNRVOVariable(true);
+
+    if (!Candidate->isNRVOVariable())
+      Return->setNRVOCandidate(nullptr);
   }
 }
 
@@ -12754,12 +12755,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl
       else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(FD))
         MarkVTableUsed(FD->getLocation(), Destructor->getParent());
 
-      // Try to apply the named return value optimization. We have to check
-      // if we can do this here because lambdas keep return statements around
-      // to deduce an implicit return type.
-      if (FD->getReturnType()->isRecordType() &&
-          (!getLangOpts().CPlusPlus || !FD->isDependentContext()))
-        computeNRVO(Body, getCurFunction());
+      // Try to apply the named return value optimization.
+      computeNRVO(Body, getCurFunction());
     }
 
     // GNU warning -Wmissing-prototypes:

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue May 29 20:53:16 2018
@@ -13385,13 +13385,9 @@ ExprResult Sema::ActOnBlockStmtExpr(Sour
   if (Body && getCurFunction()->HasPotentialAvailabilityViolations)
     DiagnoseUnguardedAvailabilityViolations(BSI->TheDecl);
 
-  // Try to apply the named return value optimization. We have to check again
-  // if we can do this, though, because blocks keep return statements around
-  // to deduce an implicit return type.
-  if (getLangOpts().CPlusPlus && RetTy->isRecordType() &&
-      !BSI->TheDecl->isDependentContext())
-    computeNRVO(Body, BSI);
-  
+  // Try to apply the named return value optimization.
+  computeNRVO(Body, BSI);
+
   BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy);
   AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
   PopFunctionScopeInfo(&WP, Result->getBlockDecl(), Result);

Modified: cfe/trunk/lib/Sema/SemaStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaStmt.cpp (original)
+++ cfe/trunk/lib/Sema/SemaStmt.cpp Tue May 29 20:53:16 2018
@@ -3455,12 +3455,9 @@ Sema::ActOnReturnStmt(SourceLocation Ret
                            ExpressionEvaluationContext::DiscardedStatement)
     return R;
 
-  if (VarDecl *VD =
-      const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate())) {
-    CurScope->addNRVOCandidate(VD);
-  } else {
-    CurScope->setNoNRVO();
-  }
+  VarDecl *VD =
+      const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate());
+  CurScope->setNRVOCandidate(VD);
 
   CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent());
 

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Tue May 29 20:53:16 2018
@@ -1326,7 +1326,7 @@ ASTDeclReader::RedeclarableResult ASTDec
     VD->NonParmVarDeclBits.IsThisDeclarationADemotedDefinition =
         Record.readInt();
     VD->NonParmVarDeclBits.ExceptionVar = Record.readInt();
-    VD->NonParmVarDeclBits.NRVOVariable = Record.readInt();
+    VD->NonParmVarDeclBits.NRVOMode = Record.readInt();
     VD->NonParmVarDeclBits.CXXForRangeDecl = Record.readInt();
     VD->NonParmVarDeclBits.ObjCForDecl = Record.readInt();
     VD->NonParmVarDeclBits.ARCPseudoStrong = Record.readInt();

Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Tue May 29 20:53:16 2018
@@ -918,7 +918,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl
   if (!isa<ParmVarDecl>(D)) {
     Record.push_back(D->isThisDeclarationADemotedDefinition());
     Record.push_back(D->isExceptionVariable());
-    Record.push_back(D->isNRVOVariable());
+    Record.push_back(D->getNRVOMode());
     Record.push_back(D->isCXXForRangeDecl());
     Record.push_back(D->isObjCForDecl());
     Record.push_back(D->isARCPseudoStrong());
@@ -2031,7 +2031,7 @@ void ASTWriter::WriteDeclAbbrevs() {
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // InitStyle
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsThisDeclarationADemotedDefinition
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable
-  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // NRVOMode
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isObjCForDecl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong

Added: cfe/trunk/test/CodeGenCXX/nrvo-noopt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/nrvo-noopt.cpp?rev=333500&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/nrvo-noopt.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/nrvo-noopt.cpp Tue May 29 20:53:16 2018
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -emit-llvm -O0 -o - %s | FileCheck %s
+
+struct X {
+  X();
+  X(X&&);
+};
+
+// CHECK-LABEL: define void @_Z7test_00b
+X test_00(bool b) {
+  if (b) {
+    // CHECK-NOT: call void @_ZN1XC1EOS_
+    // CHECK: call void @_ZN1XC1Ev
+    // CHECK-NEXT: br label %return
+    X x;
+    return x;
+  } else {
+    // CHECK-NOT: call void @_ZN1XC1EOS_
+    // CHECK: call void @_ZN1XC1Ev
+    // CHECK-NEXT: br label %return
+    X x;
+    return x;
+  }
+}
+
+// CHECK-LABEL: define void @_Z7test_01b
+X test_01(bool b) {
+  if (b) {
+    // CHECK-NOT: call void @_ZN1XC1EOS_
+    // CHECK: call void @_ZN1XC1Ev
+    // CHECK-NEXT: br label %return
+    X x;
+    return x;
+  }
+  // CHECK-NOT: call void @_ZN1XC1EOS_
+  // CHECK: call void @_ZN1XC1Ev
+  // CHECK-NEXT: br label %return
+  X x;
+  return x;
+}
+
+// CHECK-LABEL: define void @_Z7test_02b
+X test_02(bool b) {
+  // CHECK: call void @_ZN1XC1Ev
+  X x;
+
+  if (b) {
+    // CHECK-NOT: call void @_ZN1XC1EOS_
+    // CHECK: call void @_ZN1XC1Ev
+    // CHECK-NEXT: br label %return
+    X y;
+    return y;
+  }
+
+  // CHECK-NOT: call void @_ZN1XC1Ev
+  // CHECK: call void @_ZN1XC1EOS_
+  // CHECK-NEXT: br label %return
+  return x;
+}

Modified: cfe/trunk/test/CodeGenCXX/nrvo.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/nrvo.cpp?rev=333500&r1=333499&r2=333500&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/nrvo.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/nrvo.cpp Tue May 29 20:53:16 2018
@@ -130,17 +130,13 @@ X test2(bool B) {
 }
 
 // CHECK-LABEL: define void @_Z5test3b
-X test3(bool B) {
+X test3(bool B, X x) {
   // CHECK: tail call {{.*}} @_ZN1XC1Ev
-  // CHECK-NOT: call {{.*}} @_ZN1XC1ERKS_
-  // CHECK: call {{.*}} @_ZN1XC1Ev
-  // CHECK: call {{.*}} @_ZN1XC1ERKS_
   if (B) {
     X y;
     return y;
   }
-  // FIXME: we should NRVO this variable too.
-  X x;
+  // CHECK: tail call {{.*}} @_ZN1XC1ERKS_
   return x;
 }
 
@@ -191,9 +187,13 @@ X test6() {
 }
 
 // CHECK-LABEL: define void @_Z5test7b
+// CHECK-EH-LABEL: define void @_Z5test7b
 X test7(bool b) {
   // CHECK: tail call {{.*}} @_ZN1XC1Ev
   // CHECK-NEXT: ret
+
+  // CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
+  // CHECK-EH-NEXT: ret
   if (b) {
     X x;
     return x;
@@ -202,10 +202,14 @@ X test7(bool b) {
 }
 
 // CHECK-LABEL: define void @_Z5test8b
+// CHECK-EH-LABEL: define void @_Z5test8b
 X test8(bool b) {
   // CHECK: tail call {{.*}} @_ZN1XC1Ev
   // CHECK-NEXT: ret
-  if (b) {
+
+  // CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
+  // CHECK-EH-NEXT: ret
+if (b) {
     X x;
     return x;
   } else {
@@ -221,4 +225,37 @@ Y<int> test9() {
 // CHECK-LABEL: define linkonce_odr void @_ZN1YIiE1fEv
 // CHECK: tail call {{.*}} @_ZN1YIiEC1Ev
 
+// CHECK-LABEL: define void @_Z6test10b
+X test10(bool B, X x) {
+  if (B) {
+    // CHECK: tail call {{.*}} @_ZN1XC1ERKS_
+    // CHECK-EH: tail call {{.*}} @_ZN1XC1ERKS_
+    return x;
+  }
+  // CHECK: tail call {{.*}} @_ZN1XC1Ev
+  // CHECK-NOT: call {{.*}} @_ZN1XC1ERKS_
+
+  // CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
+  // CHECK-EH-NOT: call {{.*}} @_ZN1XC1ERKS_
+  X y;
+  return y;
+}
+
+// CHECK-LABEL: define {{.*}} void @_Z6test11I1XET_v
+// CHECK-EH-LABEL: define {{.*}} void @_Z6test11I1XET_v
+template <typename T>
+T test11() {
+  // CHECK:      tail call {{.*}} @_ZN1XC1Ev
+  // CHECK-NEXT: ret void
+
+  // CHECK-EH:      tail call {{.*}} @_ZN1XC1Ev
+  // CHECK-EH-NEXT: ret void
+  T t;
+  return t;
+}
+
+void test12() {
+  test11<X>();
+}
+
 // CHECK-EH-03: attributes [[NR_NUW]] = { noreturn nounwind }

Added: cfe/trunk/test/SemaCXX/nrvo-ast.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nrvo-ast.cpp?rev=333500&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/nrvo-ast.cpp (added)
+++ cfe/trunk/test/SemaCXX/nrvo-ast.cpp Tue May 29 20:53:16 2018
@@ -0,0 +1,139 @@
+// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -ast-dump -o - %s | FileCheck %s
+
+struct X {
+  X();
+  X(const X&);
+  X(X&&);
+};
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_00
+X test_00() {
+  // CHECK: VarDecl {{.*}} x {{.*}} nrvo
+  X x;
+  return x;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_01
+X test_01(bool b) {
+  // CHECK: VarDecl {{.*}} x {{.*}} nrvo
+  X x;
+  if (b)
+    return x;
+  return x;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_02
+X test_02(bool b) {
+  // CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
+  X x;
+  // CHECK-NOT: VarDecl {{.*}} y {{.*}} nrvo
+  X y;
+  if (b)
+    return y;
+  return x;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_03
+X test_03(bool b) {
+  if (b) {
+    // CHECK: VarDecl {{.*}} y {{.*}} nrvo
+    X y;
+    return y;
+  }
+  // CHECK: VarDecl {{.*}} x {{.*}} nrvo
+  X x;
+  return x;
+}
+
+extern "C" _Noreturn void exit(int) throw();
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_04
+X test_04(bool b) {
+  {
+    // CHECK: VarDecl {{.*}} x {{.*}} nrvo
+    X x;
+    if (b)
+      return x;
+  }
+  exit(1);
+}
+
+void may_throw();
+// CHECK-LABEL: FunctionDecl {{.*}} test_05
+X test_05() {
+  try {
+    may_throw();
+    return X();
+  } catch (X x) {
+    // CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
+    return x;
+  }
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_06
+X test_06() {
+  // CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
+  X x __attribute__((aligned(8)));
+  return x;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_07
+X test_07(bool b) {
+  if (b) {
+    // CHECK: VarDecl {{.*}} x {{.*}} nrvo
+    X x;
+    return x;
+  }
+  return X();
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_08
+X test_08(bool b) {
+  if (b) {
+    // CHECK: VarDecl {{.*}} x {{.*}} nrvo
+    X x;
+    return x;
+  } else {
+    // CHECK: VarDecl {{.*}} y {{.*}} nrvo
+    X y;
+    return y;
+  }
+}
+
+template <typename T>
+struct Y {
+  Y();
+  // CHECK-LABEL: CXXMethodDecl {{.*}} test_09 'Y<T> ()'
+  // CHECK: VarDecl {{.*}} y 'Y<T>' nrvo
+
+  // CHECK-LABEL: CXXMethodDecl {{.*}} test_09 'Y<int> ()'
+  // CHECK: VarDecl {{.*}} y 'Y<int>' nrvo
+  static Y test_09() {
+    Y y;
+    return y;
+  }
+};
+
+struct Z {
+  Z(const X&);
+};
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_10 'A ()'
+// CHECK: VarDecl {{.*}} b 'B' nrvo
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_10 'X ()'
+// CHECK: VarDecl {{.*}} b {{.*}} nrvo
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_10 'Z ()'
+// CHECK-NOT: VarDecl {{.*}} b {{.*}} nrvo
+template <typename A, typename B>
+A test_10() {
+  B b;
+  return b;
+}
+
+void instantiate() {
+  Y<int>::test_09();
+  test_10<X, X>();
+  test_10<Z, X>();
+}




More information about the cfe-commits mailing list