]> granicus.if.org Git - clang/commitdiff
Implement semantic analysis and an AST representation for the named
authorDouglas Gregor <dgregor@apple.com>
Sat, 15 May 2010 06:01:05 +0000 (06:01 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sat, 15 May 2010 06:01:05 +0000 (06:01 +0000)
return value optimization. Sema marks return statements with their
NRVO candidates (which may or may not end up using the NRVO), then, at
the end of a function body, computes and marks those variables that
can be allocated into the return slot.

I've checked this locally with some debugging statements (not
committed), but there won't be any tests until CodeGen comes along.

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

include/clang/AST/Decl.h
include/clang/AST/Stmt.h
lib/CodeGen/CGObjC.cpp
lib/Frontend/PCHReaderDecl.cpp
lib/Frontend/PCHReaderStmt.cpp
lib/Frontend/PCHWriterDecl.cpp
lib/Frontend/PCHWriterStmt.cpp
lib/Sema/Sema.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaStmt.cpp

index 7b2300bc4e14e51d9271d828b940f30d885202ea..a7c947afe61024a0fa0e46b0d54f16296792eea0 100644 (file)
@@ -519,6 +519,10 @@ private:
   /// or an Objective-C @catch statement.
   bool ExceptionVar : 1;
   
+  /// \brief Whether this local variable could be allocated in the return
+  /// slot of its function, enabling the named return value optimization (NRVO).
+  bool NRVOVariable : 1;
+  
   friend class StmtIteratorBase;
 protected:
   VarDecl(Kind DK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id,
@@ -526,7 +530,7 @@ protected:
           StorageClass SCAsWritten)
     : DeclaratorDecl(DK, DC, L, Id, T, TInfo), Init(),
       ThreadSpecified(false), HasCXXDirectInit(false),
-      DeclaredInCondition(false), ExceptionVar(false) {
+      DeclaredInCondition(false), ExceptionVar(false), NRVOVariable(false) {
     SClass = SC;
     SClassAsWritten = SCAsWritten;
   }
@@ -869,6 +873,19 @@ public:
   }
   void setExceptionVariable(bool EV) { ExceptionVar = EV; }
   
+  /// \brief Determine whether this local variable can be used with the named
+  /// return value optimization (NRVO).
+  ///
+  /// The named return value optimization (NRVO) works by marking certain
+  /// non-volatile local variables of class type as NRVO objects. These
+  /// locals can be allocated within the return slot of their containing
+  /// function, in which case there is no need to copy the object to the
+  /// 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.
+  bool isNRVOVariable() const { return NRVOVariable; }
+  void setNRVOVariable(bool NRVO) { NRVOVariable = NRVO; }
+  
   /// \brief If this variable is an instantiated static data member of a
   /// class template specialization, returns the templated static data member
   /// from which it was instantiated.
index bae6da00f553750d738c1cf9671c0bafb0f3a821..f95c35a1268c5864655cdc126422a5a9aa56d411 100644 (file)
@@ -1096,9 +1096,15 @@ public:
 class ReturnStmt : public Stmt {
   Stmt *RetExpr;
   SourceLocation RetLoc;
+  const VarDecl *NRVOCandidate;
+  
 public:
-  ReturnStmt(SourceLocation RL, Expr *E = 0) : Stmt(ReturnStmtClass),
-    RetExpr((Stmt*) E), RetLoc(RL) {}
+  ReturnStmt(SourceLocation RL)
+    : Stmt(ReturnStmtClass), RetExpr(0), NRVOCandidate(0) { }
+
+  ReturnStmt(SourceLocation RL, Expr *E, const VarDecl *NRVOCandidate)
+    : Stmt(ReturnStmtClass), RetExpr((Stmt*) E), RetLoc(RL),
+      NRVOCandidate(NRVOCandidate) {}
 
   /// \brief Build an empty return expression.
   explicit ReturnStmt(EmptyShell Empty) : Stmt(ReturnStmtClass, Empty) { }
@@ -1110,6 +1116,14 @@ public:
   SourceLocation getReturnLoc() const { return RetLoc; }
   void setReturnLoc(SourceLocation L) { RetLoc = L; }
 
+  /// \brief Retrieve the variable that might be used for the named return
+  /// value optimization.
+  ///
+  /// The optimization itself can only be performed if the variable is
+  /// also marked as an NRVO object.
+  const VarDecl *getNRVOCandidate() const { return NRVOCandidate; }
+  void setNRVOCandidate(const VarDecl *Var) { NRVOCandidate = Var; }
+  
   virtual SourceRange getSourceRange() const;
 
   static bool classof(const Stmt *T) {
index b3e9dc525134e0a9bc803dd310e4e1fc63a23e37..df0263ee129784bdf095427b4b26399213d96eea 100644 (file)
@@ -255,7 +255,8 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP,
         if (PID->getGetterCXXConstructor()) {
           ReturnStmt *Stmt = 
             new (getContext()) ReturnStmt(SourceLocation(), 
-                                          PID->getGetterCXXConstructor());
+                                          PID->getGetterCXXConstructor(),
+                                          0);
           EmitReturnStmt(*Stmt);
         }
         else {
index 3f91d0cafb7bbec369c4faee534349e6e0c01fc4..49010bf0d6dd171853fd0295a716e4f80834ed6b 100644 (file)
@@ -435,6 +435,7 @@ void PCHDeclReader::VisitVarDecl(VarDecl *VD) {
   VD->setCXXDirectInitializer(Record[Idx++]);
   VD->setDeclaredInCondition(Record[Idx++]);
   VD->setExceptionVariable(Record[Idx++]);
+  VD->setNRVOVariable(Record[Idx++]);
   VD->setPreviousDeclaration(
                          cast_or_null<VarDecl>(Reader.GetDecl(Record[Idx++])));
   if (Record[Idx++])
index 7595d03a15f8c241338c706e286155a2a64247fe..3931adbe8f21279f3cb922974c02d2f0ebaf5bf0 100644 (file)
@@ -291,6 +291,7 @@ unsigned PCHStmtReader::VisitReturnStmt(ReturnStmt *S) {
   VisitStmt(S);
   S->setRetValue(cast_or_null<Expr>(StmtStack.back()));
   S->setReturnLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
+  S->setNRVOCandidate(cast_or_null<VarDecl>(Reader.GetDecl(Record[Idx++])));
   return 1;
 }
 
index 28a10ad6b0a4542ff304207ffe2c5afcd002e475..cc58e8ee4654a93188b12b1a7d1e24a0e3f0c866 100644 (file)
@@ -415,6 +415,7 @@ void PCHDeclWriter::VisitVarDecl(VarDecl *D) {
   Record.push_back(D->hasCXXDirectInitializer());
   Record.push_back(D->isDeclaredInCondition());
   Record.push_back(D->isExceptionVariable());
+  Record.push_back(D->isNRVOVariable());
   Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
   Record.push_back(D->getInit() ? 1 : 0);
   if (D->getInit())
@@ -683,6 +684,7 @@ void PCHWriter::WriteDeclsBlockAbbrevs() {
   Abv->Add(BitCodeAbbrevOp(0));                       // hasCXXDirectInitializer
   Abv->Add(BitCodeAbbrevOp(0));                       // isDeclaredInCondition
   Abv->Add(BitCodeAbbrevOp(0));                       // isExceptionVariable
+  Abv->Add(BitCodeAbbrevOp(0));                       // isNRVOVariable
   Abv->Add(BitCodeAbbrevOp(0));                       // PrevDecl
   Abv->Add(BitCodeAbbrevOp(0));                       // HasInit
   // ParmVarDecl
index a7f73a17fa6affee780c88b1fc0d237b8b2dfc27..a9ee43527cec3ba5a12a7fffd306a4d28ffb9526 100644 (file)
@@ -275,6 +275,7 @@ void PCHStmtWriter::VisitReturnStmt(ReturnStmt *S) {
   VisitStmt(S);
   Writer.WriteSubStmt(S->getRetValue());
   Writer.AddSourceLocation(S->getReturnLoc(), Record);
+  Writer.AddDeclRef(S->getNRVOCandidate(), Record);
   Code = pch::STMT_RETURN;
 }
 
index 569de3203aacdf59a2d182b9be96437a8371189b..b9aa61144afa2117137adffc5021ddc5fb08091e 100644 (file)
@@ -33,6 +33,7 @@ void FunctionScopeInfo::Clear(unsigned NumErrors) {
   NeedsScopeChecking = false;
   LabelMap.clear();
   SwitchStack.clear();
+  Returns.clear();
   NumErrorsAtStartOfFunction = NumErrors;
 }
 
index d97dc676823f31efd3fcb7bbcc18394f0cbb0c40..3e143bb3b00c16d44e8abc8e8c099fd4481382f6 100644 (file)
@@ -134,6 +134,11 @@ struct FunctionScopeInfo {
   /// block.
   llvm::SmallVector<SwitchStmt*, 8> SwitchStack;
 
+  /// \brief The list of return statements that occur within the function or
+  /// block, if there is any chance of applying the named return value
+  /// optimization.
+  llvm::SmallVector<ReturnStmt *, 4> Returns;
+  
   FunctionScopeInfo(unsigned NumErrors)
     : IsBlockInfo(false), NeedsScopeChecking(false),
       NumErrorsAtStartOfFunction(NumErrors) { }
index 5bb0b524d15800fe5b2c7ecd42d08839fd7059c9..fb3100353b8a5a29223adb6863603d64977cb47a 100644 (file)
@@ -4548,6 +4548,38 @@ Sema::DeclPtrTy Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, DeclPtrTy D) {
   return DeclPtrTy::make(FD);
 }
 
+/// \brief Given the set of return statements within a function body,
+/// compute the variables that are subject to the named return value 
+/// 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
+/// 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 function has the same NRVO candidate, that candidate is
+/// the NRVO variable.
+///
+/// FIXME: Employ a smarter algorithm that accounts for multiple return 
+/// statements and the lifetimes of the NRVO candidates. We should be able to
+/// find a maximal set of NRVO variables.
+static void ComputeNRVO(Stmt *Body, ReturnStmt **Returns, unsigned NumReturns) {
+  const VarDecl *NRVOCandidate = 0;
+  for (unsigned I = 0; I != NumReturns; ++I) {
+    if (!Returns[I]->getNRVOCandidate())
+      return;
+    
+    if (!NRVOCandidate)
+      NRVOCandidate = Returns[I]->getNRVOCandidate();
+    else if (NRVOCandidate != Returns[I]->getNRVOCandidate())
+      return;
+  }
+  
+  if (NRVOCandidate)
+    const_cast<VarDecl*>(NRVOCandidate)->setNRVOVariable(true);
+}
+
 Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg) {
   return ActOnFinishFunctionBody(D, move(BodyArg), false);
 }
@@ -4581,6 +4613,9 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,
       // If this is a constructor, we need a vtable.
       if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(FD))
         MarkVTableUsed(FD->getLocation(), Constructor->getParent());
+      
+      ComputeNRVO(Body, FunctionScopes.back()->Returns.data(),
+                  FunctionScopes.back()->Returns.size());
     }
     
     assert(FD == getCurFunctionDecl() && "Function parsing confused");
index d9210f6a1b0ef78c85cb3931b422c24fb952bef5..f8fe4fe21c04bdd73898dade54f908a2daba46ce 100644 (file)
@@ -1058,29 +1058,42 @@ Sema::ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope) {
   return Owned(new (Context) BreakStmt(BreakLoc));
 }
 
-/// IsReturnCopyElidable - Whether returning @p RetExpr from a function that
-/// returns a @p RetType fulfills the criteria for copy elision (C++0x 12.8p34).
-static bool IsReturnCopyElidable(ASTContext &Ctx, QualType RetType,
-                                 Expr *RetExpr) {
+/// \brief Determine whether a return statement is a candidate for the named
+/// return value optimization (C++0x 12.8p34, bullet 1).
+///
+/// \param Ctx The context in which the return expression and type occur.
+///
+/// \param RetType The return type of the function or block.
+///
+/// \param RetExpr The expression being returned from the function or block.
+///
+/// \returns The NRVO candidate variable, if the return statement may use the
+/// NRVO, or NULL if there is no such candidate.
+static const VarDecl *getNRVOCandidate(ASTContext &Ctx, QualType RetType,
+                                       Expr *RetExpr) {
   QualType ExprType = RetExpr->getType();
   // - in a return statement in a function with ...
   // ... a class return type ...
   if (!RetType->isRecordType())
-    return false;
+    return 0;
   // ... the same cv-unqualified type as the function return type ...
   if (!Ctx.hasSameUnqualifiedType(RetType, ExprType))
-    return false;
+    return 0;
   // ... the expression is the name of a non-volatile automatic object ...
   // We ignore parentheses here.
   // FIXME: Is this compliant? (Everyone else does it)
   const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(RetExpr->IgnoreParens());
   if (!DR)
-    return false;
+    return 0;
   const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
   if (!VD)
-    return false;
-  return VD->getKind() == Decl::Var && VD->hasLocalStorage() && 
-    !VD->getType()->isReferenceType() && !VD->getType().isVolatileQualified();
+    return 0;
+  
+  if (VD->getKind() == Decl::Var && VD->hasLocalStorage() && 
+      !VD->getType()->isReferenceType() && !VD->getType().isVolatileQualified())
+    return VD;
+  
+  return 0;
 }
 
 /// ActOnBlockReturnStmt - Utility routine to figure out block's return type.
@@ -1117,46 +1130,58 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
   // Otherwise, verify that this result type matches the previous one.  We are
   // pickier with blocks than for normal functions because we don't have GCC
   // compatibility to worry about here.
+  ReturnStmt *Result = 0;
   if (CurBlock->ReturnType->isVoidType()) {
     if (RetValExp) {
       Diag(ReturnLoc, diag::err_return_block_has_expr);
       RetValExp->Destroy(Context);
       RetValExp = 0;
     }
-    return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
-  }
-
-  if (!RetValExp)
+    Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, 0);
+  } else if (!RetValExp) {
     return StmtError(Diag(ReturnLoc, diag::err_block_return_missing_expr));
+  } else {
+    const VarDecl *NRVOCandidate = 0;
+    
+    if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
+      // we have a non-void block with an expression, continue checking
+
+      // C99 6.8.6.4p3(136): The return statement is not an assignment. The
+      // overlap restriction of subclause 6.5.16.1 does not apply to the case of
+      // function return.
+
+      // In C++ the return statement is handled via a copy initialization.
+      // the C version of which boils down to CheckSingleAssignmentConstraints.
+      NRVOCandidate = getNRVOCandidate(Context, FnRetType, RetValExp);
+      OwningExprResult Res = PerformCopyInitialization(
+                               InitializedEntity::InitializeResult(ReturnLoc, 
+                                                                   FnRetType,
+                                                            NRVOCandidate != 0),
+                               SourceLocation(),
+                               Owned(RetValExp));
+      if (Res.isInvalid()) {
+        // FIXME: Cleanup temporaries here, anyway?
+        return StmtError();
+      }
+      
+      if (RetValExp)
+        RetValExp = MaybeCreateCXXExprWithTemporaries(RetValExp);
 
-  if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
-    // we have a non-void block with an expression, continue checking
-
-    // C99 6.8.6.4p3(136): The return statement is not an assignment. The
-    // overlap restriction of subclause 6.5.16.1 does not apply to the case of
-    // function return.
-
-    // In C++ the return statement is handled via a copy initialization.
-    // the C version of which boils down to CheckSingleAssignmentConstraints.
-    OwningExprResult Res = PerformCopyInitialization(
-                             InitializedEntity::InitializeResult(ReturnLoc, 
-                                                                 FnRetType,
-                                               IsReturnCopyElidable(Context, 
-                                                                    FnRetType, 
-                                                                    RetValExp)),
-                             SourceLocation(),
-                             Owned(RetValExp));
-    if (Res.isInvalid()) {
-      // FIXME: Cleanup temporaries here, anyway?
-      return StmtError();
+      RetValExp = Res.takeAs<Expr>();
+      if (RetValExp) 
+        CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
     }
     
-    RetValExp = Res.takeAs<Expr>();
-    if (RetValExp) 
-      CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
+    Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, NRVOCandidate);
   }
 
-  return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
+  // If we need to check for the named return value optimization, save the 
+  // return statement in our scope for later processing.
+  if (getLangOptions().CPlusPlus && FnRetType->isRecordType() &&
+      !CurContext->isDependentContext())
+    FunctionScopes.back()->Returns.push_back(Result);
+  
+  return Owned(Result);
 }
 
 Action::OwningStmtResult
@@ -1177,6 +1202,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) {
   else // If we don't have a function/method context, bail.
     return StmtError();
 
+  ReturnStmt *Result = 0;
   if (FnRetType->isVoidType()) {
     if (RetValExp && !RetValExp->isTypeDependent()) {
       // C99 6.8.6.4p1 (ext_ since GCC warns)
@@ -1195,10 +1221,9 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) {
 
       RetValExp = MaybeCreateCXXExprWithTemporaries(RetValExp);
     }
-    return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
-  }
-
-  if (!RetValExp && !FnRetType->isDependentType()) {
+    
+    Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, 0);
+  } else if (!RetValExp && !FnRetType->isDependentType()) {
     unsigned DiagID = diag::warn_return_missing_expr;  // C90 6.6.6.4p4
     // C99 6.8.6.4p1 (ext_ since GCC warns)
     if (getLangOptions().C99) DiagID = diag::ext_return_missing_expr;
@@ -1207,43 +1232,47 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) {
       Diag(ReturnLoc, DiagID) << FD->getIdentifier() << 0/*fn*/;
     else
       Diag(ReturnLoc, DiagID) << getCurMethodDecl()->getDeclName() << 1/*meth*/;
-    return Owned(new (Context) ReturnStmt(ReturnLoc, (Expr*)0));
-  }
-
-  if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
-    // we have a non-void function with an expression, continue checking
-
-    // C99 6.8.6.4p3(136): The return statement is not an assignment. The
-    // overlap restriction of subclause 6.5.16.1 does not apply to the case of
-    // function return.
+    Result = new (Context) ReturnStmt(ReturnLoc);
+  } else {
+    const VarDecl *NRVOCandidate = 0;
+    if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
+      // we have a non-void function with an expression, continue checking
+
+      // C99 6.8.6.4p3(136): The return statement is not an assignment. The
+      // overlap restriction of subclause 6.5.16.1 does not apply to the case of
+      // function return.
+
+      // In C++ the return statement is handled via a copy initialization.
+      // the C version of which boils down to CheckSingleAssignmentConstraints.
+      NRVOCandidate = getNRVOCandidate(Context, FnRetType, RetValExp);
+      OwningExprResult Res = PerformCopyInitialization(
+                               InitializedEntity::InitializeResult(ReturnLoc, 
+                                                                   FnRetType,
+                                                            NRVOCandidate != 0),
+                               SourceLocation(),
+                               Owned(RetValExp));
+      if (Res.isInvalid()) {
+        // FIXME: Cleanup temporaries here, anyway?
+        return StmtError();
+      }
 
-    // C++0x [class.copy]p34:
-    //
-    
-    
-    // In C++ the return statement is handled via a copy initialization.
-    // the C version of which boils down to CheckSingleAssignmentConstraints.
-    OwningExprResult Res = PerformCopyInitialization(
-                             InitializedEntity::InitializeResult(ReturnLoc, 
-                                                                 FnRetType,
-                                               IsReturnCopyElidable(Context, 
-                                                                    FnRetType, 
-                                                                    RetValExp)),
-                             SourceLocation(),
-                             Owned(RetValExp));
-    if (Res.isInvalid()) {
-      // FIXME: Cleanup temporaries here, anyway?
-      return StmtError();
+      RetValExp = Res.takeAs<Expr>();
+      if (RetValExp) 
+        CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
     }
-
-    RetValExp = Res.takeAs<Expr>();
-    if (RetValExp) 
-      CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
+    
+    if (RetValExp)
+      RetValExp = MaybeCreateCXXExprWithTemporaries(RetValExp);
+    Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, NRVOCandidate);
   }
-
-  if (RetValExp)
-    RetValExp = MaybeCreateCXXExprWithTemporaries(RetValExp);
-  return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
+  
+  // If we need to check for the named return value optimization, save the 
+  // return statement in our scope for later processing.
+  if (getLangOptions().CPlusPlus && FnRetType->isRecordType() &&
+      !CurContext->isDependentContext())
+    FunctionScopes.back()->Returns.push_back(Result);
+  
+  return Owned(Result);
 }
 
 /// CheckAsmLValue - GNU C has an extremely ugly extension whereby they silently