[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