[clang] d30ca5e - [C++20] [Coroutines] Implement return value optimization for get_return_object

Chuanqi Xu via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 15 21:38:19 PST 2022


Author: Chuanqi Xu
Date: 2022-02-16T13:38:00+08:00
New Revision: d30ca5e2e23fe50dcd8d5d602bf7cfc61b4c1561

URL: https://github.com/llvm/llvm-project/commit/d30ca5e2e23fe50dcd8d5d602bf7cfc61b4c1561
DIFF: https://github.com/llvm/llvm-project/commit/d30ca5e2e23fe50dcd8d5d602bf7cfc61b4c1561.diff

LOG: [C++20] [Coroutines] Implement return value optimization for get_return_object

This patch tries to implement RVO for coroutine's return object got from
get_return_object.
>From [dcl.fct.def.coroutine]/p7 we could know that the return value of
get_return_object is either a reference or a prvalue. So it makes sense
to do copy elision for the return value. The return object should be
constructed directly into the storage where they would otherwise be
copied/moved to.

Test Plan: folly, check-all

Reviewed By: junparser

Differential revision: https://reviews.llvm.org/D117087

Added: 
    clang/test/CodeGenCoroutines/coro-gro2-exp-namespace.cpp
    clang/test/CodeGenCoroutines/coro-gro2.cpp
    clang/test/SemaCXX/coroutine-no-move-ctor.cpp

Modified: 
    clang/include/clang/AST/StmtCXX.h
    clang/lib/AST/StmtCXX.cpp
    clang/lib/CodeGen/CGCoroutine.cpp
    clang/lib/Sema/SemaCoroutine.cpp
    clang/lib/Sema/TreeTransform.h
    clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp
    clang/test/CodeGenCoroutines/coro-alloc.cpp
    clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp
    clang/test/CodeGenCoroutines/coro-gro.cpp
    clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp
    clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
    clang/test/SemaCXX/coroutines-exp-namespace.cpp
    clang/test/SemaCXX/coroutines.cpp

Removed: 
    clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp
    clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp


################################################################################
diff  --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index 4d1f3e8ef255c..5ccf6904048e3 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -327,7 +327,6 @@ class CoroutineBodyStmt final
     Allocate,      ///< Coroutine frame memory allocation.
     Deallocate,    ///< Coroutine frame memory deallocation.
     ReturnValue,   ///< Return value for thunk function: p.get_return_object().
-    ResultDecl,    ///< Declaration holding the result of get_return_object.
     ReturnStmt,    ///< Return statement for the thunk function.
     ReturnStmtOnAllocFailure, ///< Return statement if allocation failed.
     FirstParamMove ///< First offset for move construction of parameter copies.
@@ -354,7 +353,6 @@ class CoroutineBodyStmt final
     Expr *Allocate = nullptr;
     Expr *Deallocate = nullptr;
     Expr *ReturnValue = nullptr;
-    Stmt *ResultDecl = nullptr;
     Stmt *ReturnStmt = nullptr;
     Stmt *ReturnStmtOnAllocFailure = nullptr;
     ArrayRef<Stmt *> ParamMoves;
@@ -409,7 +407,11 @@ class CoroutineBodyStmt final
   Expr *getReturnValueInit() const {
     return cast<Expr>(getStoredStmts()[SubStmt::ReturnValue]);
   }
-  Stmt *getResultDecl() const { return getStoredStmts()[SubStmt::ResultDecl]; }
+  Expr *getReturnValue() const {
+    assert(getReturnStmt());
+    auto *RS = cast<clang::ReturnStmt>(getReturnStmt());
+    return RS->getRetValue();
+  }
   Stmt *getReturnStmt() const { return getStoredStmts()[SubStmt::ReturnStmt]; }
   Stmt *getReturnStmtOnAllocFailure() const {
     return getStoredStmts()[SubStmt::ReturnStmtOnAllocFailure];

diff  --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 060d090fc06ac..33b0421ad1016 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -118,7 +118,6 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args)
   SubStmts[CoroutineBodyStmt::Allocate] = Args.Allocate;
   SubStmts[CoroutineBodyStmt::Deallocate] = Args.Deallocate;
   SubStmts[CoroutineBodyStmt::ReturnValue] = Args.ReturnValue;
-  SubStmts[CoroutineBodyStmt::ResultDecl] = Args.ResultDecl;
   SubStmts[CoroutineBodyStmt::ReturnStmt] = Args.ReturnStmt;
   SubStmts[CoroutineBodyStmt::ReturnStmtOnAllocFailure] =
       Args.ReturnStmtOnAllocFailure;

diff  --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index c1763cbbc5a05..96696ebf2903c 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -465,72 +465,6 @@ struct CallCoroDelete final : public EHScopeStack::Cleanup {
 };
 }
 
-namespace {
-struct GetReturnObjectManager {
-  CodeGenFunction &CGF;
-  CGBuilderTy &Builder;
-  const CoroutineBodyStmt &S;
-
-  Address GroActiveFlag;
-  CodeGenFunction::AutoVarEmission GroEmission;
-
-  GetReturnObjectManager(CodeGenFunction &CGF, const CoroutineBodyStmt &S)
-      : CGF(CGF), Builder(CGF.Builder), S(S), GroActiveFlag(Address::invalid()),
-        GroEmission(CodeGenFunction::AutoVarEmission::invalid()) {}
-
-  // The gro variable has to outlive coroutine frame and coroutine promise, but,
-  // it can only be initialized after coroutine promise was created, thus, we
-  // split its emission in two parts. EmitGroAlloca emits an alloca and sets up
-  // cleanups. Later when coroutine promise is available we initialize the gro
-  // and sets the flag that the cleanup is now active.
-
-  void EmitGroAlloca() {
-    auto *GroDeclStmt = dyn_cast<DeclStmt>(S.getResultDecl());
-    if (!GroDeclStmt) {
-      // If get_return_object returns void, no need to do an alloca.
-      return;
-    }
-
-    auto *GroVarDecl = cast<VarDecl>(GroDeclStmt->getSingleDecl());
-
-    // Set GRO flag that it is not initialized yet
-    GroActiveFlag =
-      CGF.CreateTempAlloca(Builder.getInt1Ty(), CharUnits::One(), "gro.active");
-    Builder.CreateStore(Builder.getFalse(), GroActiveFlag);
-
-    GroEmission = CGF.EmitAutoVarAlloca(*GroVarDecl);
-
-    // Remember the top of EHStack before emitting the cleanup.
-    auto old_top = CGF.EHStack.stable_begin();
-    CGF.EmitAutoVarCleanups(GroEmission);
-    auto top = CGF.EHStack.stable_begin();
-
-    // Make the cleanup conditional on gro.active
-    for (auto b = CGF.EHStack.find(top), e = CGF.EHStack.find(old_top);
-      b != e; b++) {
-      if (auto *Cleanup = dyn_cast<EHCleanupScope>(&*b)) {
-        assert(!Cleanup->hasActiveFlag() && "cleanup already has active flag?");
-        Cleanup->setActiveFlag(GroActiveFlag);
-        Cleanup->setTestFlagInEHCleanup();
-        Cleanup->setTestFlagInNormalCleanup();
-      }
-    }
-  }
-
-  void EmitGroInit() {
-    if (!GroActiveFlag.isValid()) {
-      // No Gro variable was allocated. Simply emit the call to
-      // get_return_object.
-      CGF.EmitStmt(S.getResultDecl());
-      return;
-    }
-
-    CGF.EmitAutoVarInit(GroEmission);
-    Builder.CreateStore(Builder.getTrue(), GroActiveFlag);
-  }
-};
-}
-
 static void emitBodyAndFallthrough(CodeGenFunction &CGF,
                                    const CoroutineBodyStmt &S, Stmt *Body) {
   CGF.EmitStmt(Body);
@@ -597,13 +531,6 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
       CGM.getIntrinsic(llvm::Intrinsic::coro_begin), {CoroId, Phi});
   CurCoro.Data->CoroBegin = CoroBegin;
 
-  // We need to emit `get_­return_­object` first. According to:
-  // [dcl.fct.def.coroutine]p7
-  // The call to get_­return_­object is sequenced before the call to
-  // initial_­suspend and is invoked at most once.
-  GetReturnObjectManager GroManager(*this, S);
-  GroManager.EmitGroAlloca();
-
   CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(RetBB);
   {
     CGDebugInfo *DI = getDebugInfo();
@@ -641,8 +568,23 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
     // promise local variable was not emitted yet.
     CoroId->setArgOperand(1, PromiseAddrVoidPtr);
 
-    // Now we have the promise, initialize the GRO
-    GroManager.EmitGroInit();
+    // ReturnValue should be valid as long as the coroutine's return type
+    // is not void. The assertion could help us to reduce the check later.
+    assert(ReturnValue.isValid() == (bool)S.getReturnStmt());
+    // Now we have the promise, initialize the GRO.
+    // We need to emit `get_return_object` first. According to:
+    // [dcl.fct.def.coroutine]p7
+    // The call to get_return_­object is sequenced before the call to
+    // initial_suspend and is invoked at most once.
+    //
+    // So we couldn't emit return value when we emit return statment,
+    // otherwise the call to get_return_object wouldn't be in front
+    // of initial_suspend.
+    if (ReturnValue.isValid()) {
+      EmitAnyExprToMem(S.getReturnValue(), ReturnValue,
+                       S.getReturnValue()->getType().getQualifiers(),
+                       /*IsInit*/ true);
+    }
 
     EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
 
@@ -705,8 +647,12 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
   llvm::Function *CoroEnd = CGM.getIntrinsic(llvm::Intrinsic::coro_end);
   Builder.CreateCall(CoroEnd, {NullPtr, Builder.getFalse()});
 
-  if (Stmt *Ret = S.getReturnStmt())
+  if (Stmt *Ret = S.getReturnStmt()) {
+    // Since we already emitted the return value above, so we shouldn't
+    // emit it again here.
+    cast<ReturnStmt>(Ret)->setRetValue(nullptr);
     EmitStmt(Ret);
+  }
 
   // LLVM require the frontend to add the function attribute. See
   // Coroutines.rst.

diff  --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 71fcf4aebda84..a7375f0909b89 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1577,7 +1577,6 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() {
     if (Res.isInvalid())
       return false;
 
-    this->ResultDecl = Res.get();
     return true;
   }
 
@@ -1590,51 +1589,11 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() {
     return false;
   }
 
-  auto *GroDecl = VarDecl::Create(
-      S.Context, &FD, FD.getLocation(), FD.getLocation(),
-      &S.PP.getIdentifierTable().get("__coro_gro"), GroType,
-      S.Context.getTrivialTypeSourceInfo(GroType, Loc), SC_None);
-  GroDecl->setImplicit();
-
-  S.CheckVariableDeclarationType(GroDecl);
-  if (GroDecl->isInvalidDecl())
-    return false;
-
-  InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl);
-  ExprResult Res =
-      S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue);
-  if (Res.isInvalid())
-    return false;
-
-  Res = S.ActOnFinishFullExpr(Res.get(), /*DiscardedValue*/ false);
-  if (Res.isInvalid())
-    return false;
-
-  S.AddInitializerToDecl(GroDecl, Res.get(),
-                         /*DirectInit=*/false);
-
-  S.FinalizeDeclaration(GroDecl);
-
-  // Form a declaration statement for the return declaration, so that AST
-  // visitors can more easily find it.
-  StmtResult GroDeclStmt =
-      S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(GroDecl), Loc, Loc);
-  if (GroDeclStmt.isInvalid())
-    return false;
-
-  this->ResultDecl = GroDeclStmt.get();
-
-  ExprResult declRef = S.BuildDeclRefExpr(GroDecl, GroType, VK_LValue, Loc);
-  if (declRef.isInvalid())
-    return false;
-
-  StmtResult ReturnStmt = S.BuildReturnStmt(Loc, declRef.get());
+  StmtResult ReturnStmt = S.BuildReturnStmt(Loc, ReturnValue);
   if (ReturnStmt.isInvalid()) {
     noteMemberDeclaredHere(S, ReturnValue, Fn);
     return false;
   }
-  if (cast<clang::ReturnStmt>(ReturnStmt.get())->getNRVOCandidate() == GroDecl)
-    GroDecl->setNRVOVariable(true);
 
   this->ReturnStmt = ReturnStmt.get();
   return true;

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index facb4f3a49448..466a156add516 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7903,12 +7903,6 @@ TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) {
       return StmtError();
     Builder.Deallocate = DeallocRes.get();
 
-    assert(S->getResultDecl() && "ResultDecl must already be built");
-    StmtResult ResultDecl = getDerived().TransformStmt(S->getResultDecl());
-    if (ResultDecl.isInvalid())
-      return StmtError();
-    Builder.ResultDecl = ResultDecl.get();
-
     if (auto *ReturnStmt = S->getReturnStmt()) {
       StmtResult Res = getDerived().TransformStmt(ReturnStmt);
       if (Res.isInvalid())

diff  --git a/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp
index 442c12e41ff2d..612978fc55b72 100644
--- a/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp
+++ b/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp
@@ -226,7 +226,6 @@ struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> {
 // CHECK-LABEL: f4(
 extern "C" int f4(promise_on_alloc_failure_tag) {
   // CHECK: %[[RetVal:.+]] = alloca i32
-  // CHECK: %[[Gro:.+]] = alloca i32
   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
   // CHECK: %[[MEM:.+]] = call noalias noundef i8* @_ZnwmRKSt9nothrow_t(i64 noundef %[[SIZE]], %"struct.std::nothrow_t"* noundef nonnull align 1 dereferenceable(1) @_ZStL7nothrow)
@@ -240,13 +239,6 @@ extern "C" int f4(promise_on_alloc_failure_tag) {
 
   // CHECK: [[OKBB]]:
   // CHECK:   %[[OkRet:.+]] = call noundef i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv(
-  // CHECK:   store i32 %[[OkRet]], i32* %[[Gro]]
-
-  // CHECK: %[[Tmp1:.*]] = load i32, i32* %[[Gro]]
-  // CHECK-NEXT: store i32 %[[Tmp1]], i32* %[[RetVal]]
-  // CHECK-NEXT: %[[Gro_CAST:.+]] = bitcast i32* %[[Gro]] to i8*
-  // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* %[[Gro_CAST]]) #2
-  // CHECK-NEXT: br label %[[RetBB]]
 
   // CHECK: [[RetBB]]:
   // CHECK:   %[[LoadRet:.+]] = load i32, i32* %[[RetVal]], align 4

diff  --git a/clang/test/CodeGenCoroutines/coro-alloc.cpp b/clang/test/CodeGenCoroutines/coro-alloc.cpp
index 634e652829245..d6f9cebab83fc 100644
--- a/clang/test/CodeGenCoroutines/coro-alloc.cpp
+++ b/clang/test/CodeGenCoroutines/coro-alloc.cpp
@@ -224,7 +224,6 @@ struct std::coroutine_traits<int, promise_on_alloc_failure_tag> {
 // CHECK-LABEL: f4(
 extern "C" int f4(promise_on_alloc_failure_tag) {
   // CHECK: %[[RetVal:.+]] = alloca i32
-  // CHECK: %[[Gro:.+]] = alloca i32
   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
   // CHECK: %[[MEM:.+]] = call noalias noundef i8* @_ZnwmRKSt9nothrow_t(i64 noundef %[[SIZE]], %"struct.std::nothrow_t"* noundef nonnull align 1 dereferenceable(1) @_ZStL7nothrow)
@@ -238,13 +237,6 @@ extern "C" int f4(promise_on_alloc_failure_tag) {
 
   // CHECK: [[OKBB]]:
   // CHECK:   %[[OkRet:.+]] = call noundef i32 @_ZNSt16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv(
-  // CHECK:   store i32 %[[OkRet]], i32* %[[Gro]]
-
-  // CHECK: %[[Tmp1:.*]] = load i32, i32* %[[Gro]]
-  // CHECK-NEXT: store i32 %[[Tmp1]], i32* %[[RetVal]]
-  // CHECK-NEXT: %[[Gro_CAST:.+]] = bitcast i32* %[[Gro]] to i8*
-  // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* %[[Gro_CAST]]) #2
-  // CHECK-NEXT: br label %[[RetBB]]
 
   // CHECK: [[RetBB]]:
   // CHECK:   %[[LoadRet:.+]] = load i32, i32* %[[RetVal]], align 4

diff  --git a/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp
index c5ee0a18e0fbe..ec4b87ac88983 100644
--- a/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp
@@ -48,14 +48,13 @@ void doSomething() noexcept;
 // CHECK: define{{.*}} i32 @_Z1fv(
 int f() {
   // CHECK: %[[RetVal:.+]] = alloca i32
-  // CHECK: %[[GroActive:.+]] = alloca i1
 
   // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
   // CHECK: call noalias noundef nonnull i8* @_Znwm(i64 noundef %[[Size]])
-  // CHECK: store i1 false, i1* %[[GroActive]]
   // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_typeC1Ev(
-  // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type17get_return_objectEv(
-  // CHECK: store i1 true, i1* %[[GroActive]]
+  // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type17get_return_objectEv(%struct.GroType* sret(%struct.GroType) align {{[0-9]+}} %[[GRO:.+]],
+  // CHECK: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv({{.*}}[[GRO]]
+  // CHECK: store i32 %[[Conv]], i32* %[[RetVal]]
 
   Cleanup cleanup;
   doSomething();
@@ -71,18 +70,7 @@ int f() {
   // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free(
   // CHECK: call void @_ZdlPv(i8* noundef %[[Mem]])
 
-  // Initialize retval from Gro and destroy Gro
-
-  // CHECK: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv(
-  // CHECK: store i32 %[[Conv]], i32* %[[RetVal]]
-  // CHECK: %[[IsActive:.+]] = load i1, i1* %[[GroActive]]
-  // CHECK: br i1 %[[IsActive]], label %[[CleanupGro:.+]], label %[[Done:.+]]
-
-  // CHECK: [[CleanupGro]]:
-  // CHECK:   call void @_ZN7GroTypeD1Ev(
-  // CHECK:   br label %[[Done]]
-
-  // CHECK: [[Done]]:
+  // CHECK: coro.ret:
   // CHECK:   %[[LoadRet:.+]] = load i32, i32* %[[RetVal]]
   // CHECK:   ret i32 %[[LoadRet]]
 }

diff  --git a/clang/test/CodeGenCoroutines/coro-gro.cpp b/clang/test/CodeGenCoroutines/coro-gro.cpp
index 1adf2137eafcd..45bb845c5af3b 100644
--- a/clang/test/CodeGenCoroutines/coro-gro.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro.cpp
@@ -46,14 +46,13 @@ void doSomething() noexcept;
 // CHECK: define{{.*}} i32 @_Z1fv(
 int f() {
   // CHECK: %[[RetVal:.+]] = alloca i32
-  // CHECK: %[[GroActive:.+]] = alloca i1
 
   // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
   // CHECK: call noalias noundef nonnull i8* @_Znwm(i64 noundef %[[Size]])
-  // CHECK: store i1 false, i1* %[[GroActive]]
   // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_typeC1Ev(
-  // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_type17get_return_objectEv(
-  // CHECK: store i1 true, i1* %[[GroActive]]
+  // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_type17get_return_objectEv(%struct.GroType* sret(%struct.GroType) align {{[0-9]+}} %[[GRO:.+]],
+  // CHECK: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv({{.*}}[[GRO]]
+  // CHECK: store i32 %[[Conv]], i32* %[[RetVal]]
 
   Cleanup cleanup;
   doSomething();
@@ -69,18 +68,7 @@ int f() {
   // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free(
   // CHECK: call void @_ZdlPv(i8* noundef %[[Mem]])
 
-  // Initialize retval from Gro and destroy Gro
-
-  // CHECK: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv(
-  // CHECK: store i32 %[[Conv]], i32* %[[RetVal]]
-  // CHECK: %[[IsActive:.+]] = load i1, i1* %[[GroActive]]
-  // CHECK: br i1 %[[IsActive]], label %[[CleanupGro:.+]], label %[[Done:.+]]
-
-  // CHECK: [[CleanupGro]]:
-  // CHECK:   call void @_ZN7GroTypeD1Ev(
-  // CHECK:   br label %[[Done]]
-
-  // CHECK: [[Done]]:
+  // CHECK: coro.ret:
   // CHECK:   %[[LoadRet:.+]] = load i32, i32* %[[RetVal]]
   // CHECK:   ret i32 %[[LoadRet]]
 }

diff  --git a/clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-gro2-exp-namespace.cpp
similarity index 91%
rename from clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp
rename to clang/test/CodeGenCoroutines/coro-gro2-exp-namespace.cpp
index 6dc536e7601dc..a601aad8a78c5 100644
--- a/clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro2-exp-namespace.cpp
@@ -32,16 +32,14 @@ struct coro {
   Impl *impl;
 };
 
-// Verify that the NRVO is applied to the Gro object.
+// Verify that the RVO is applied.
 // CHECK-LABEL: define{{.*}} void @_Z1fi(%struct.coro* noalias sret(%struct.coro) align 8 %agg.result, i32 noundef %0)
 coro f(int) {
   // CHECK: %call = call noalias noundef nonnull i8* @_Znwm(
   // CHECK-NEXT: br label %[[CoroInit:.*]]
 
   // CHECK: {{.*}}[[CoroInit]]:
-  // CHECK: store i1 false, i1* %gro.active
   // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro* sret(%struct.coro) align 8 %agg.result
-  // CHECK-NEXT: store i1 true, i1* %gro.active
   co_return;
 }
 
@@ -75,9 +73,7 @@ coro_two h(int) {
   // CHECK-NEXT: br label %[[RetLabel:.*]]
 
   // CHECK: {{.*}}[[InitOnSuccess]]:
-  // CHECK: store i1 false, i1* %gro.active
   // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result
-  // CHECK-NEXT: store i1 true, i1* %gro.active
 
   // CHECK: [[RetLabel]]:
   // CHECK-NEXT: ret void

diff  --git a/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp b/clang/test/CodeGenCoroutines/coro-gro2.cpp
similarity index 66%
rename from clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
rename to clang/test/CodeGenCoroutines/coro-gro2.cpp
index 3b11e1f49ce6d..58519dab3e5a8 100644
--- a/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro2.cpp
@@ -33,17 +33,15 @@ struct coro {
   Impl *impl;
 };
 
-// Verify that the NRVO is applied to the Gro object.
+// Verify that the RVO is applied.
 // CHECK-LABEL: define{{.*}} void @_Z1fi(%struct.coro* noalias sret(%struct.coro) align 8 %agg.result, i32 noundef %0)
 coro f(int) {
 // CHECK: %call = call noalias noundef nonnull i8* @_Znwm(
 // CHECK-NEXT: br label %[[CoroInit:.*]]
 
 // CHECK: {{.*}}[[CoroInit]]:
-// CHECK: store i1 false, i1* %gro.active
 // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro* sret(%struct.coro) align 8 %agg.result
-// CHECK-NEXT: store i1 true, i1* %gro.active
-  co_return;
+co_return;
 }
 
 
@@ -64,24 +62,22 @@ struct coro_two {
   Impl *impl;
 };
 
-// Verify that the NRVO is applied to the Gro object.
+// Verify that the RVO is applied.
 // CHECK-LABEL: define{{.*}} void @_Z1hi(%struct.coro_two* noalias sret(%struct.coro_two) align 8 %agg.result, i32 noundef %0)
- coro_two h(int) {
+coro_two h(int) {
 
-// CHECK: %call = call noalias noundef i8* @_ZnwmRKSt9nothrow_t
-// CHECK-NEXT: %[[CheckNull:.*]] = icmp ne i8* %call, null
-// CHECK-NEXT: br i1 %[[CheckNull]], label %[[InitOnSuccess:.*]], label %[[InitOnFailure:.*]]
+  // CHECK: %call = call noalias noundef i8* @_ZnwmRKSt9nothrow_t
+  // CHECK-NEXT: %[[CheckNull:.*]] = icmp ne i8* %call, null
+  // CHECK-NEXT: br i1 %[[CheckNull]], label %[[InitOnSuccess:.*]], label %[[InitOnFailure:.*]]
 
-// CHECK: {{.*}}[[InitOnFailure]]:
-// CHECK-NEXT: call void @{{.*get_return_object_on_allocation_failureEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result
-// CHECK-NEXT: br label %[[RetLabel:.*]]
+  // CHECK: {{.*}}[[InitOnFailure]]:
+  // CHECK-NEXT: call void @{{.*get_return_object_on_allocation_failureEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result
+  // CHECK-NEXT: br label %[[RetLabel:.*]]
 
-// CHECK: {{.*}}[[InitOnSuccess]]:
-// CHECK: store i1 false, i1* %gro.active
-// CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result
-// CHECK-NEXT: store i1 true, i1* %gro.active
+  // CHECK: {{.*}}[[InitOnSuccess]]:
+  // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result
 
-// CHECK: [[RetLabel]]:
-// CHECK-NEXT: ret void
+  // CHECK: [[RetLabel]]:
+  // CHECK-NEXT: ret void
   co_return;
 }

diff  --git a/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp
index 1e7897544440a..2406f8997ec7a 100644
--- a/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp
+++ b/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp
@@ -31,19 +31,8 @@ coro_t f() {
 }
 
 // CHECK-LABEL: define dso_local void @"?f@@YA?AUcoro_t@@XZ"(
-// CHECK:  %gro.active = alloca i1
-// CHECK:  store i1 false, i1* %gro.active
 
 // CHECK:  invoke noundef %"struct.coro_t::promise_type"* @"??0promise_type at coro_t@@QEAA at XZ"(
 // CHECK:  invoke void @"?get_return_object at promise_type@coro_t@@QEAA?AU2 at XZ"(
-// CHECK:  store i1 true, i1* %gro.active
 
-// CHECK:  %[[IS_ACTIVE:.+]] = load i1, i1* %gro.active
-// CHECK:  br i1 %[[IS_ACTIVE]], label %[[CLEANUP1:.+]], label
-
-// CHECK: [[CLEANUP1]]:
-// CHECK:  %[[NRVO:.+]] = load i1, i1* %nrvo
-// CHECK:  br i1 %[[NRVO]], label %{{.+}}, label %[[DTOR:.+]]
-
-// CHECK: [[DTOR]]:
-// CHECK:  call void @"??1coro_t@@QEAA at XZ"(
+// CHECK:  call void @"??1promise_type at coro_t@@QEAA at XZ"

diff  --git a/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp b/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
index 13ba249767fee..5a4dd02074e0a 100644
--- a/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
+++ b/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp
@@ -27,19 +27,8 @@ coro_t f() {
 }
 
 // CHECK-LABEL: define dso_local void @"?f@@YA?AUcoro_t@@XZ"(
-// CHECK:  %gro.active = alloca i1
-// CHECK:  store i1 false, i1* %gro.active
 
 // CHECK:  invoke noundef %"struct.coro_t::promise_type"* @"??0promise_type at coro_t@@QEAA at XZ"(
 // CHECK:  invoke void @"?get_return_object at promise_type@coro_t@@QEAA?AU2 at XZ"(
-// CHECK:  store i1 true, i1* %gro.active
 
-// CHECK:  %[[IS_ACTIVE:.+]] = load i1, i1* %gro.active
-// CHECK:  br i1 %[[IS_ACTIVE]], label %[[CLEANUP1:.+]], label
-
-// CHECK: [[CLEANUP1]]:
-// CHECK:  %[[NRVO:.+]] = load i1, i1* %nrvo
-// CHECK:  br i1 %[[NRVO]], label %{{.+}}, label %[[DTOR:.+]]
-
-// CHECK: [[DTOR]]:
-// CHECK:  call void @"??1coro_t@@QEAA at XZ"(
+// CHECK:  call void @"??1promise_type at coro_t@@QEAA at XZ"

diff  --git a/clang/test/SemaCXX/coroutine-no-move-ctor.cpp b/clang/test/SemaCXX/coroutine-no-move-ctor.cpp
new file mode 100644
index 0000000000000..824dea375ebde
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-no-move-ctor.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -verify
+// expected-no-diagnostics
+
+#include "Inputs/std-coroutine.h"
+
+class invoker {
+public:
+  class invoker_promise {
+  public:
+    invoker get_return_object() { return invoker{}; }
+    auto initial_suspend() { return std::suspend_never{}; }
+    auto final_suspend() noexcept { return std::suspend_never{}; }
+    void return_void() {}
+    void unhandled_exception() {}
+  };
+  using promise_type = invoker_promise;
+  invoker() {}
+  invoker(const invoker &) = delete;
+  invoker &operator=(const invoker &) = delete;
+  invoker(invoker &&) = delete;
+  invoker &operator=(invoker &&) = delete;
+};
+
+invoker f() {
+  co_return;
+}

diff  --git a/clang/test/SemaCXX/coroutines-exp-namespace.cpp b/clang/test/SemaCXX/coroutines-exp-namespace.cpp
index caa141367d87f..534d47dfa776e 100644
--- a/clang/test/SemaCXX/coroutines-exp-namespace.cpp
+++ b/clang/test/SemaCXX/coroutines-exp-namespace.cpp
@@ -939,7 +939,7 @@ struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag2> {
 
 extern "C" int f(mismatch_gro_type_tag2) {
   // cxx2b-error at -1 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}}
-  // cxx14_20-error at -2 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}}
+  // cxx14_20-error at -2 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}}
   co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
 }
 

diff  --git a/clang/test/SemaCXX/coroutines.cpp b/clang/test/SemaCXX/coroutines.cpp
index c81385bcc0595..5d6ef7f73cfd7 100644
--- a/clang/test/SemaCXX/coroutines.cpp
+++ b/clang/test/SemaCXX/coroutines.cpp
@@ -929,7 +929,7 @@ struct std::coroutine_traits<int, mismatch_gro_type_tag2> {
 
 extern "C" int f(mismatch_gro_type_tag2) {
   // cxx2b-error at -1 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}}
-  // cxx14_20-error at -2 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}}
+  // cxx14_20-error at -2 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}}
   co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
 }
 


        


More information about the cfe-commits mailing list