[clang] 565e37c - [Coroutines] Fix code coverage for coroutine
Author: Xun Li Date: 2020-07-01T10:11:40-07:00 New Revision: 565e37c7702d181804c12d36b6010c513c9b3417 URL: https://github.com/llvm/llvm-project/commit/565e37c7702d181804c12d36b6010c513c9b3417 DIFF: https://github.com/llvm/llvm-project/commit/565e37c7702d181804c12d36b6010c513c9b3417.diff LOG: [Coroutines] Fix code coverage for coroutine Summary: Previously, source-based coverage analysis does not work properly for coroutine. This patch adds processing of coroutine body and co_return in the coverage analysis, so that we can handle them properly. For coroutine body, we should only look at the actual function body and ignore the compiler-generated things; for co_return, we need to terminate the region similar to return statement. Added a test, and confirms that it now works properly. (without this patch, the statement after the if statement will be treated wrongly) Reviewers: lewissbaker, modocache, junparser Reviewed By: modocache Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D82928 Added: clang/test/CoverageMapping/coroutine.cpp Modified: clang/lib/CodeGen/CoverageMappingGen.cpp Removed: diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp index cdbfc88e7b70..78b268f423cb 100644 --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -908,6 +908,18 @@ struct CounterCoverageMappingBuilder terminateRegion(S); } + void VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) { +extendRegion(S); +Visit(S->getBody()); + } + + void VisitCoreturnStmt(const CoreturnStmt *S) { +extendRegion(S); +if (S->getOperand()) + Visit(S->getOperand()); +terminateRegion(S); + } + void VisitCXXThrowExpr(const CXXThrowExpr *E) { extendRegion(E); if (E->getSubExpr()) diff --git a/clang/test/CoverageMapping/coroutine.cpp b/clang/test/CoverageMapping/coroutine.cpp new file mode 100644 index ..ec1d64e0f970 --- /dev/null +++ b/clang/test/CoverageMapping/coroutine.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping %s | FileCheck %s + +namespace std::experimental { +template +struct coroutine_traits; + +template +struct coroutine_handle { + coroutine_handle() = default; + static coroutine_handle from_address(void *) noexcept { return {}; } +}; +template <> +struct coroutine_handle { + static coroutine_handle from_address(void *) { return {}; } + coroutine_handle() = default; + template + coroutine_handle(coroutine_handle) noexcept {} +}; +} // namespace std::experimental + +struct suspend_always { + bool await_ready() noexcept; + void await_suspend(std::experimental::coroutine_handle<>) noexcept; + void await_resume() noexcept; +}; + +template <> +struct std::experimental::coroutine_traits { + struct promise_type { +int get_return_object(); +suspend_always initial_suspend(); +suspend_always final_suspend() noexcept; +void return_value(int); + }; +}; + +// CHECK-LABEL: _Z2f1i: +int f1(int x) { // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE+7]]:2 = #0 + if (x > 42) { // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = #0 +++x; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:14 -> [[@LINE-1]]:15 = #1 + } else {// CHECK-NEXT: File 0, [[@LINE-2]]:15 -> [[@LINE]]:4 = #1 +co_return x + 42; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:4 -> [[@LINE-1]]:10 = (#0 - #1) + } // CHECK-NEXT: File 0, [[@LINE-2]]:10 -> [[@LINE]]:4 = (#0 - #1) + co_return x;// CHECK-NEXT: Gap,File 0, [[@LINE-1]]:4 -> [[@LINE]]:3 = #1 +} // CHECK-NEXT: File 0, [[@LINE-1]]:3 -> [[@LINE]]:2 = #1 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] ddcf063 - [Coroutines] Fix test breakage in D82928
Author: Xun Li Date: 2020-07-01T11:17:21-07:00 New Revision: ddcf063dd52ff1f30ac27d6f9abce6a45685a2b2 URL: https://github.com/llvm/llvm-project/commit/ddcf063dd52ff1f30ac27d6f9abce6a45685a2b2 DIFF: https://github.com/llvm/llvm-project/commit/ddcf063dd52ff1f30ac27d6f9abce6a45685a2b2.diff LOG: [Coroutines] Fix test breakage in D82928 Summary: The test file in D82928 generated temp files within the test directory, causing test failures. Fix it. Reviewers: modocache, fhahn Reviewed By: modocache Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D82986 Added: Modified: clang/test/CoverageMapping/coroutine.cpp Removed: diff --git a/clang/test/CoverageMapping/coroutine.cpp b/clang/test/CoverageMapping/coroutine.cpp index ec1d64e0f970..c149eefd1f01 100644 --- a/clang/test/CoverageMapping/coroutine.cpp +++ b/clang/test/CoverageMapping/coroutine.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping %s -o - | FileCheck %s namespace std::experimental { template ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 9fc8772 - clang CoverageMapping tests bot cleanup
Author: Xun Li Date: 2020-07-01T16:06:56-07:00 New Revision: 9fc877213e075a76831fe71291d7c072c64c27e3 URL: https://github.com/llvm/llvm-project/commit/9fc877213e075a76831fe71291d7c072c64c27e3 DIFF: https://github.com/llvm/llvm-project/commit/9fc877213e075a76831fe71291d7c072c64c27e3.diff LOG: clang CoverageMapping tests bot cleanup Summary: D82928 generated unexpected tmp files in the CoverageMapping test directory. This patch cleans it up and remove the file in the test bots. It will be revered after a week. Reviewers: thakis Reviewed By: thakis Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D82992 Added: Modified: clang/test/CoverageMapping/coroutine.cpp Removed: diff --git a/clang/test/CoverageMapping/coroutine.cpp b/clang/test/CoverageMapping/coroutine.cpp index c149eefd1f01..dc9473348fc9 100644 --- a/clang/test/CoverageMapping/coroutine.cpp +++ b/clang/test/CoverageMapping/coroutine.cpp @@ -1,3 +1,5 @@ +// fixme: the following line is added to cleanup bots, will be removed in weeks. +// RUN: rm -f %S/coroutine.ll // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping %s -o - | FileCheck %s namespace std::experimental { ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 516803d - [Coroutines] Ensure co_await promise.final_suspend() does not throw
Author: Xun Li Date: 2020-06-22T15:01:42-07:00 New Revision: 516803dc8685ebcc5bce38b05391958ffee22643 URL: https://github.com/llvm/llvm-project/commit/516803dc8685ebcc5bce38b05391958ffee22643 DIFF: https://github.com/llvm/llvm-project/commit/516803dc8685ebcc5bce38b05391958ffee22643.diff LOG: [Coroutines] Ensure co_await promise.final_suspend() does not throw Summary: This patch addresses https://bugs.llvm.org/show_bug.cgi?id=46256 The spec of coroutine requires that the expression co_await promise.final_suspend() shall not be potentially-throwing. To check this, we recursively look at every call (including Call, MemberCall, OperatorCall and Constructor) in all code generated by the final suspend, and ensure that the callees are declared with noexcept. We also look at any returned data type that requires explicit destruction, and check their destructors for noexcept. This patch does not check declarations with dependent types yet, which will be done in future patches. Updated all tests to add noexcept to the required functions, and added a dedicated test for this patch. This patch might start to cause existing codebase fail to compile because most people may not have been strict in tagging all the related functions noexcept. Reviewers: lewissbaker, modocache, junparser Reviewed By: modocache Subscribers: arphaman, junparser, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D82029 Added: clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp Modified: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaCoroutine.cpp clang/lib/Sema/SemaExceptionSpec.cpp clang/test/AST/Inputs/std-coroutine.h clang/test/AST/coroutine-source-location-crash.cpp clang/test/Analysis/more-dtors-cfg-output.cpp clang/test/CodeGenCXX/ubsan-coroutines.cpp clang/test/CodeGenCoroutines/Inputs/coroutine.h clang/test/CodeGenCoroutines/coro-alloc.cpp clang/test/CodeGenCoroutines/coro-always-inline.cpp clang/test/CodeGenCoroutines/coro-await-domination.cpp clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp clang/test/CodeGenCoroutines/coro-await.cpp clang/test/CodeGenCoroutines/coro-dest-slot.cpp clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp clang/test/CodeGenCoroutines/coro-params.cpp clang/test/CodeGenCoroutines/coro-promise-dtor.cpp clang/test/CodeGenCoroutines/coro-ret-void.cpp clang/test/CodeGenCoroutines/coro-return-voidtype-initlist.cpp clang/test/CodeGenCoroutines/coro-return.cpp clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp clang/test/Index/coroutines.cpp clang/test/SemaCXX/Inputs/std-coroutine.h clang/test/SemaCXX/co_await-range-for.cpp clang/test/SemaCXX/coreturn-eh.cpp clang/test/SemaCXX/coreturn.cpp clang/test/SemaCXX/coroutine-rvo.cpp clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp clang/test/SemaCXX/coroutine-uninitialized-warning-crash.cpp clang/test/SemaCXX/coroutines.cpp Removed: diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d6d0ccaa00be..66856834a98f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10523,7 +10523,13 @@ def err_await_suspend_invalid_return_type : Error< def note_await_ready_no_bool_conversion : Note< "return type of 'await_ready' is required to be contextually convertible to 'bool'" >; -} +def err_coroutine_promise_final_suspend_requires_nothrow : Error< + "the expression 'co_await __promise.final_suspend()' is required to be non-throwing" +>; +def note_coroutine_function_declare_noexcept : Note< + "must be declared with 'noexcept'" +>; +} // end of coroutines issue category let CategoryName = "Documentation Issue" in { def warn_not_a_doxygen_trailing_member_comment : Warning< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 8c8e981e6065..88dd0d453883 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1697,6 +1697,10 @@ class Sema final { static QualType GetTypeFromParser(ParsedType Ty, TypeSourceInfo **TInfo = nullptr); CanThrowResult canThrow(const Stmt *E); + /// Determine whether the callee of a particular function call can throw. + /// E, D and Loc are all optional. + static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D, + SourceLocation Loc = SourceLocation()); const FunctionProtoType *ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT); void UpdateExceptionSpec(FunctionDecl *FD, diff --git a/clang
[clang] 3661595 - [Coroutines] Special handle __builtin_coro_resume for final_suspend nothrow check
Author: Xun Li Date: 2020-06-25T10:49:50-07:00 New Revision: 366159566df3a980d3e34f3ec9609e77cdb4df8b URL: https://github.com/llvm/llvm-project/commit/366159566df3a980d3e34f3ec9609e77cdb4df8b DIFF: https://github.com/llvm/llvm-project/commit/366159566df3a980d3e34f3ec9609e77cdb4df8b.diff LOG: [Coroutines] Special handle __builtin_coro_resume for final_suspend nothrow check Summary: In https://reviews.llvm.org/D82029 we added the conformance check that the expression co_await promise.final_suspend() should not potentially throw. As part of this expression, in cases when the await_suspend() method of the final suspend awaiter returns a handle, __builtin_coro_resume could be called on the handle to immediately resume that coroutine. __builtin_coro_resume is not declared with noexcept and it shouldn't. We need to special check this case here. Reviewers: modocache, lewissbaker, junparser Reviewed By: lewissbaker Subscribers: modocache, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D82415 Added: Modified: clang/lib/Sema/SemaCoroutine.cpp Removed: diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index c8ca247aae83..6262f4b117d3 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -614,6 +614,17 @@ static void checkNoThrow(Sema &S, const Stmt *E, // In the case of dtor, the call to dtor is implicit and hence we should // pass nullptr to canCalleeThrow. if (Sema::canCalleeThrow(S, IsDtor ? nullptr : cast(E), D)) { + if (const auto *FD = dyn_cast(D)) { +// co_await promise.final_suspend() could end up calling +// __builtin_coro_resume for symmetric transfer if await_suspend() +// returns a handle. In that case, even __builtin_coro_resume is not +// declared as noexcept and may throw, it does not throw _into_ the +// coroutine that just suspended, but rather throws back out from +// whoever called coroutine_handle::resume(), hence we claim that +// logically it does not throw. +if (FD->getBuiltinID() == Builtin::BI__builtin_coro_resume) + return; + } if (ThrowingDecls.empty()) { // First time seeing an error, emit the error message. S.Diag(cast(S.CurContext)->getLocation(), ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] c25acec - [Coroutines] Handle dependent promise types for final_suspend non-throw check
Author: Xun Li Date: 2020-06-25T11:27:27-07:00 New Revision: c25acec84594ca15748553341969f8e579290e27 URL: https://github.com/llvm/llvm-project/commit/c25acec84594ca15748553341969f8e579290e27 DIFF: https://github.com/llvm/llvm-project/commit/c25acec84594ca15748553341969f8e579290e27.diff LOG: [Coroutines] Handle dependent promise types for final_suspend non-throw check Summary: Check that the co_await promise.final_suspend() does not potentially throw again after we have resolved dependent types. This takes care of the cases where promises types are templated. Added test cases for this scenario and confirmed that the checks happen now. Also run libcxx tests locally to make sure all tests pass. Reviewers: Benabik, lewissbaker, junparser, modocache Reviewed By: modocache Subscribers: modocache, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D82332 Added: Modified: clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaCoroutine.cpp clang/lib/Sema/TreeTransform.h clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp Removed: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 19c68423e5da..f3024efa293c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9804,6 +9804,9 @@ class Sema final { void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body); ClassTemplateDecl *lookupCoroutineTraits(SourceLocation KwLoc, SourceLocation FuncLoc); + /// Check that the expression co_await promise.final_suspend() shall not be + /// potentially-throwing. + bool checkFinalSuspendNoThrow(const Stmt *FinalSuspend); //======// // OpenCL extensions. diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 6262f4b117d3..70b8fd282056 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -642,7 +642,6 @@ static void checkNoThrow(Sema &S, const Stmt *E, } else if (SC == Expr::CallExprClass || SC == Expr::CXXMemberCallExprClass || SC == Expr::CXXOperatorCallExprClass) { if (!cast(E)->isTypeDependent()) { - // FIXME: Handle dependent types. checkDeclNoexcept(cast(E)->getCalleeDecl()); auto ReturnType = cast(E)->getCallReturnType(S.getASTContext()); // Check the destructor of the call return type, if any. @@ -662,22 +661,20 @@ static void checkNoThrow(Sema &S, const Stmt *E, } } -/// Check that the expression co_await promise.final_suspend() shall not be -/// potentially-throwing. -static bool checkNoThrow(Sema &S, const Stmt *FinalSuspend) { +bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) { llvm::SmallPtrSet ThrowingDecls; // We first collect all declarations that should not throw but not declared // with noexcept. We then sort them based on the location before printing. // This is to avoid emitting the same note multiple times on the same // declaration, and also provide a deterministic order for the messages. - checkNoThrow(S, FinalSuspend, ThrowingDecls); + checkNoThrow(*this, FinalSuspend, ThrowingDecls); auto SortedDecls = llvm::SmallVector{ThrowingDecls.begin(), ThrowingDecls.end()}; sort(SortedDecls, [](const Decl *A, const Decl *B) { return A->getEndLoc() < B->getEndLoc(); }); for (const auto *D : SortedDecls) { -S.Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept); +Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept); } return ThrowingDecls.empty(); } @@ -724,7 +721,7 @@ bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc, return true; StmtResult FinalSuspend = buildSuspends("final_suspend"); - if (FinalSuspend.isInvalid() || !checkNoThrow(*this, FinalSuspend.get())) + if (FinalSuspend.isInvalid() || !checkFinalSuspendNoThrow(FinalSuspend.get())) return true; ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get()); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index ff9d4d610660..ce9c30339617 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7630,7 +7630,8 @@ TreeTransform::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) { return StmtError(); StmtResult FinalSuspend = getDerived().TransformStmt(S->getFinalSuspendStmt()); - if (FinalSuspend.isInvalid()) + if (FinalSuspend.isInvalid() || + !SemaRef.checkFinalSuspendNoThrow(FinalSuspend.get())) return StmtError(); ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get()); assert(isa(InitSuspend.get()) && isa(FinalSuspend.get())); diff --git a/clang/test/SemaCXX/coroutine-final-suspend-noexc
[clang] df477db - [Coroutine][Sema] Tighten the lifetime of symmetric transfer returned handle
Author: Xun Li Date: 2020-09-11T13:35:37-07:00 New Revision: df477db5f9e0ea2a4890040b65002d93e33209b0 URL: https://github.com/llvm/llvm-project/commit/df477db5f9e0ea2a4890040b65002d93e33209b0 DIFF: https://github.com/llvm/llvm-project/commit/df477db5f9e0ea2a4890040b65002d93e33209b0.diff LOG: [Coroutine][Sema] Tighten the lifetime of symmetric transfer returned handle In generating the code for symmetric transfer, a temporary object is created to store the returned handle from await_suspend() call of the awaiter. Previously this temp won't be cleaned up until very later, which ends up causing this temp to be spilled to the heap. However, we know that this temp will no longer be needed after the coro_resume call. We can clean it up right after. Differential Revision: https://reviews.llvm.org/D87470 Added: clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp Modified: clang/lib/Sema/SemaCoroutine.cpp clang/test/CodeGenCoroutines/Inputs/coroutine.h Removed: diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 990ab2633520..565f907e05b2 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -398,6 +398,10 @@ static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E, diag::warn_coroutine_handle_address_invalid_return_type) << JustAddress->getType(); + // The coroutine handle used to obtain the address is no longer needed + // at this point, clean it up to avoid unnecessarily long lifetime which + // could lead to unnecessary spilling. + JustAddress = S.MaybeCreateExprWithCleanups(JustAddress); return buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_resume, JustAddress); } diff --git a/clang/test/CodeGenCoroutines/Inputs/coroutine.h b/clang/test/CodeGenCoroutines/Inputs/coroutine.h index 5cc78a4904aa..2dd1ce7e9735 100644 --- a/clang/test/CodeGenCoroutines/Inputs/coroutine.h +++ b/clang/test/CodeGenCoroutines/Inputs/coroutine.h @@ -15,7 +15,7 @@ template <> struct coroutine_handle { return me; } void operator()() { resume(); } - void *address() const { return ptr; } + void *address() const noexcept { return ptr; } void resume() const { __builtin_coro_resume(ptr); } void destroy() const { __builtin_coro_destroy(ptr); } bool done() const { return __builtin_coro_done(ptr); } diff --git a/clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp b/clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp new file mode 100644 index ..09205799c3f7 --- /dev/null +++ b/clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp @@ -0,0 +1,53 @@ +// RUN: %clang -std=c++14 -fcoroutines-ts -emit-llvm -S -O1 %s -o - + +#include "Inputs/coroutine.h" + +namespace coro = std::experimental::coroutines_v1; + +struct detached_task { + struct promise_type { +detached_task get_return_object() noexcept { + return detached_task{coro::coroutine_handle::from_promise(*this)}; +} + +void return_void() noexcept {} + +struct final_awaiter { + bool await_ready() noexcept { return false; } + coro::coroutine_handle<> await_suspend(coro::coroutine_handle h) noexcept { +h.destroy(); +return {}; + } + void await_resume() noexcept {} +}; + +void unhandled_exception() noexcept {} + +final_awaiter final_suspend() noexcept { return {}; } + +coro::suspend_always initial_suspend() noexcept { return {}; } + }; + + ~detached_task() { +if (coro_) { + coro_.destroy(); + coro_ = {}; +} + } + + void start() && { +auto tmp = coro_; +coro_ = {}; +tmp.resume(); + } + + coro::coroutine_handle coro_; +}; + +detached_task foo() { + co_return; +} + +// check that the lifetime of the coroutine handle used to obtain the address ended right away. +// CHECK: %{{.*}} = call i8* @{{.*address.*}}(%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* nonnull %{{.*}}) +// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %{{.*}}) ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 19f0770 - [Coroutine][Sema] Cleanup temporaries as early as possible
Author: Xun Li Date: 2020-11-10T13:27:42-08:00 New Revision: 19f07709234304b0214f5352750e85cacfda4b36 URL: https://github.com/llvm/llvm-project/commit/19f07709234304b0214f5352750e85cacfda4b36 DIFF: https://github.com/llvm/llvm-project/commit/19f07709234304b0214f5352750e85cacfda4b36.diff LOG: [Coroutine][Sema] Cleanup temporaries as early as possible The original bug was discovered in T75057860. Clang front-end emits an AST that looks like this for an co_await expression: |- ExprWithCleanups |- -CoawaitExpr |- -MaterializeTemporaryExpr ... Awaiter ... |- -CXXMemberCallExpr ... .await_ready ... |- -CallExpr ... __builtin_coro_resume ... |- -CXXMemberCallExpr ... .await_resume ... ExprWithCleanups is responsible for cleaning up (including calling dtors) for the temporaries generated in the wrapping expression). In the above structure, the __builtin_coro_resume part (which corresponds to the code for the suspend case in the co_await with symmetric transfer), the pseudocode looks like this: __builtin_coro_resume( awaiter.await_suspend( from_address( __builtin_coro_frame())).address()); One of the temporaries that's generated as part of this code is the coroutine handle returned from awaiter.await_suspend() call. The call returns a handle which is a prvalue (since it's a returned value on the fly). In order to call the address() method on it, it needs to be converted into an xvalue. Hence a materialized temp is created to hold it. This temp will need to be cleaned up eventually. Now, since all cleanups happen at the end of the entire co_await expression, which is after the suspension point, the compiler will think that such a temp needs to live across suspensions, and need to be put on the coroutine frame, even though it's only used temporarily just to call address() method. Such a phenomena not only unnecessarily increases the frame size, but can lead to ASAN failures, if the coroutine was already destroyed as part of the await_suspend() call. This is because if the coroutine was already destroyed, the frame no longer exists, and one can not store anything into it. But if the temporary object is considered to need to live on the frame, it will be stored into the frame after await_suspend() returns. A fix attempt was done in https://reviews.llvm.org/D87470. Unfortunately it is incorrect. The reason is that cleanups in Clang works more like linearly than nested. There is one current state indicating whether it needs cleanup, and an ExprWithCleanups resets that state. This means that an ExprWithCleanups must be capable of cleaning up all temporaries created in the wrapping expression, otherwise there will be dangling temporaries cleaned up at the wrong place. I eventually found a walk-around (https://reviews.llvm.org/D89066) that doesn't break any existing tests while fixing the issue. But it targets the final co_await only. If we ever have a co_await that's not on the final awaiter and the frame gets destroyed after suspend, we are in trouble. Hence we need a proper fix. This patch is the proper fix. It does the folllowing things to fully resolve the issue: 1. The AST has to be generated in the order according to their nesting relationship. We should not generate AST out of order because then the code generator would incorrectly track the state of temporaries and when a cleanup is needed. So the code in buildCoawaitCalls is reorganized so that we will be generating the AST for each coawait member call in order along with their child AST. 2. await_ready() call is wrapped with an ExprWithCleanups so that temporaries in it gets cleaned up as early as possible to avoid living across suspension. 3. await_suspend() call is wrapped with an ExprWithCleanups if it's not a symmetric transfer. In the case of a symmetric transfer, in order to maintain the musttail call contract, the ExprWithCleanups is wraaped before the resume call. 4. In the end, we mark again that it needs a cleanup, so that the entire CoawaitExpr will be wrapped with a ExprWithCleanups which will clean up the Awaiter object associated with the await expression. Differential Revision: https://reviews.llvm.org/D90990 Added: clang/test/AST/coroutine-locals-cleanup.cpp clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp clang/test/CodeGenCoroutines/coro-symmetric-transfer-02.cpp Modified: clang/lib/Sema/SemaCoroutine.cpp clang/test/AST/Inputs/std-coroutine.h Removed: clang/test/CodeGenCoroutines/coro-symmetric-transfer.cpp diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 5582c728aa2d..ce4e55873734 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -398,51 +398,55 @@ static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E, diag::warn_co
[clang] c6c8d4a - [modules] Fix crash in call to `FunctionDecl::setPure()`
Author: Andrew Gallagher Date: 2020-11-18T11:55:29-08:00 New Revision: c6c8d4a13ebd5ce1c3c7e8632312ab8c2dc6afa0 URL: https://github.com/llvm/llvm-project/commit/c6c8d4a13ebd5ce1c3c7e8632312ab8c2dc6afa0 DIFF: https://github.com/llvm/llvm-project/commit/c6c8d4a13ebd5ce1c3c7e8632312ab8c2dc6afa0.diff LOG: [modules] Fix crash in call to `FunctionDecl::setPure()` In some cases, when deserializing a `CXXMethodDecl` of a `CXXSpecializationTemplateDecl`, the call to `FunctionDecl::setPure()` happens before the `DefinitionData` member has been populated (which appears to happen lower down in a `mergeRedeclarable` call), causing a crash (https://reviews.llvm.org/P8228). This diff fixes this by deferring the `FunctionDecl::setPure()` till after the `DefinitionData` has been filled in. Reviewed By: lxfind Differential Revision: https://reviews.llvm.org/D86853 Added: clang/test/Modules/Inputs/set-pure-crash/a.h clang/test/Modules/Inputs/set-pure-crash/b.h clang/test/Modules/Inputs/set-pure-crash/c.h clang/test/Modules/Inputs/set-pure-crash/module.modulemap clang/test/Modules/set-pure-crash.cpp Modified: clang/lib/Serialization/ASTReaderDecl.cpp Removed: diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 797232885687..6bfb9bd783b5 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -868,7 +868,10 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { FD->setInlineSpecified(Record.readInt()); FD->setImplicitlyInline(Record.readInt()); FD->setVirtualAsWritten(Record.readInt()); - FD->setPure(Record.readInt()); + // We defer calling `FunctionDecl::setPure()` here as for methods of + // `CXXTemplateSpecializationDecl`s, we may not have connected up the + // definition (which is required for `setPure`). + const bool Pure = Record.readInt(); FD->setHasInheritedPrototype(Record.readInt()); FD->setHasWrittenPrototype(Record.readInt()); FD->setDeletedAsWritten(Record.readInt()); @@ -1015,6 +1018,10 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { } } + // Defer calling `setPure` until merging above has guaranteed we've set + // `DefinitionData` (as this will need to access it). + FD->setPure(Pure); + // Read in the parameters. unsigned NumParams = Record.readInt(); SmallVector Params; diff --git a/clang/test/Modules/Inputs/set-pure-crash/a.h b/clang/test/Modules/Inputs/set-pure-crash/a.h new file mode 100644 index ..f52458e0daeb --- /dev/null +++ b/clang/test/Modules/Inputs/set-pure-crash/a.h @@ -0,0 +1,11 @@ +#pragma once + +struct Tag {}; + +template +class Base { +public: + virtual void func() = 0; +}; + +Base bar(); diff --git a/clang/test/Modules/Inputs/set-pure-crash/b.h b/clang/test/Modules/Inputs/set-pure-crash/b.h new file mode 100644 index ..eef7c2b9faaa --- /dev/null +++ b/clang/test/Modules/Inputs/set-pure-crash/b.h @@ -0,0 +1,14 @@ +#pragma once + +#include "a.h" +#include "c.h" + +template > +void foo(Fun) {} + +class Child : public Base { +public: + void func() { +foo([]() {}); + } +}; diff --git a/clang/test/Modules/Inputs/set-pure-crash/c.h b/clang/test/Modules/Inputs/set-pure-crash/c.h new file mode 100644 index ..d5b7cd19461f --- /dev/null +++ b/clang/test/Modules/Inputs/set-pure-crash/c.h @@ -0,0 +1,5 @@ +#pragma once + +template +struct simple { +}; diff --git a/clang/test/Modules/Inputs/set-pure-crash/module.modulemap b/clang/test/Modules/Inputs/set-pure-crash/module.modulemap new file mode 100644 index ..50bbe84e5d07 --- /dev/null +++ b/clang/test/Modules/Inputs/set-pure-crash/module.modulemap @@ -0,0 +1,11 @@ +module a { + header "a.h" +} + +module b { + header "b.h" +} + +module c { + header "c.h" +} diff --git a/clang/test/Modules/set-pure-crash.cpp b/clang/test/Modules/set-pure-crash.cpp new file mode 100644 index ..197e0cb00d27 --- /dev/null +++ b/clang/test/Modules/set-pure-crash.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x c++ -I %S/Inputs/set-pure-crash -verify %s -o %t + +// expected-no-diagnostics + +#include "b.h" +#include "c.h" + +auto t = simple(); ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] dce8f2b - [Coroutine][Sema] Only tighten the suspend call temp lifetime for final awaiter
Author: Xun Li Date: 2020-10-12T12:00:20-07:00 New Revision: dce8f2bb25ea1d01533d8e602f2520492fa67259 URL: https://github.com/llvm/llvm-project/commit/dce8f2bb25ea1d01533d8e602f2520492fa67259 DIFF: https://github.com/llvm/llvm-project/commit/dce8f2bb25ea1d01533d8e602f2520492fa67259.diff LOG: [Coroutine][Sema] Only tighten the suspend call temp lifetime for final awaiter In https://reviews.llvm.org/D87470 I added the change to tighten the lifetime of the expression awaiter.await_suspend().address. Howver it was incorrect. ExprWithCleanups will call the dtor and end the lifetime for all the temps created in the current full expr. When this is called on a normal await call, we don't want to do that. We only want to do this for the call on the final_awaiter, to avoid writing into the frame after the frame is destroyed. This change fixes it, by checking IsImplicit. Differential Revision: https://reviews.llvm.org/D89066 Added: Modified: clang/lib/Sema/SemaCoroutine.cpp clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp Removed: diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 565f907e05b2..5582c728aa2d 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -375,7 +375,7 @@ static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc, // returning await_suspend that results in a guaranteed tail call to the target // coroutine. static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E, - SourceLocation Loc) { + SourceLocation Loc, bool IsImplicit) { if (RetType->isReferenceType()) return nullptr; Type const *T = RetType.getTypePtr(); @@ -398,10 +398,17 @@ static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E, diag::warn_coroutine_handle_address_invalid_return_type) << JustAddress->getType(); - // The coroutine handle used to obtain the address is no longer needed - // at this point, clean it up to avoid unnecessarily long lifetime which - // could lead to unnecessary spilling. - JustAddress = S.MaybeCreateExprWithCleanups(JustAddress); + // After the await_suspend call on the awaiter, the coroutine may have + // been destroyed. In that case, we can not store anything to the frame + // from this point on. Hence here we wrap it immediately with a cleanup. This + // could have applied to all await_suspend calls. However doing so causes + // alive objects being destructed for reasons that need further + // investigations. Here we walk-around it temporarily by only doing it after + // the suspend call on the final awaiter (indicated by IsImplicit) where it's + // most common to happen. + // TODO: Properly clean up the temps generated by await_suspend calls. + if (IsImplicit) +JustAddress = S.MaybeCreateExprWithCleanups(JustAddress); return buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_resume, JustAddress); } @@ -409,7 +416,8 @@ static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E, /// Build calls to await_ready, await_suspend, and await_resume for a co_await /// expression. static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise, - SourceLocation Loc, Expr *E) { + SourceLocation Loc, Expr *E, + bool IsImplicit) { OpaqueValueExpr *Operand = new (S.Context) OpaqueValueExpr(Loc, E->getType(), VK_LValue, E->getObjectKind(), E); @@ -458,7 +466,8 @@ static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise, QualType RetType = AwaitSuspend->getCallReturnType(S.Context); // Experimental support for coroutine_handle returning await_suspend. -if (Expr *TailCallSuspend = maybeTailCall(S, RetType, AwaitSuspend, Loc)) +if (Expr *TailCallSuspend = +maybeTailCall(S, RetType, AwaitSuspend, Loc, IsImplicit)) Calls.Results[ACT::ACT_Suspend] = TailCallSuspend; else { // non-class prvalues always have cv-unqualified types @@ -870,8 +879,8 @@ ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E, SourceLocation CallLoc = E->getExprLoc(); // Build the await_ready, await_suspend, await_resume calls. - ReadySuspendResumeResult RSS = - buildCoawaitCalls(*this, Coroutine->CoroutinePromise, CallLoc, E); + ReadySuspendResumeResult RSS = buildCoawaitCalls( + *this, Coroutine->CoroutinePromise, CallLoc, E, IsImplicit); if (RSS.IsInvalid) return ExprError(); @@ -925,8 +934,8 @@ ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) { E = CreateMaterializeTemporaryExpr(E->getType(), E, true); // Build the await_ready, await_suspend, await_resume calls. - Rea
[clang] d80ecdf - [Coroutine] Rename coro-semmetric-transfer.cpp and possibly fix test failure
Author: Xun Li Date: 2020-10-12T15:29:07-07:00 New Revision: d80ecdf27faf2c45a4264064ddfd5c4524dadce4 URL: https://github.com/llvm/llvm-project/commit/d80ecdf27faf2c45a4264064ddfd5c4524dadce4 DIFF: https://github.com/llvm/llvm-project/commit/d80ecdf27faf2c45a4264064ddfd5c4524dadce4.diff LOG: [Coroutine] Rename coro-semmetric-transfer.cpp and possibly fix test failure Some tests start to fail after https://reviews.llvm.org/D89066. It's because the size of pointers are different on different targets. Limit the target in the command so there is no confusion. Also noticed I had typo in the test name. Adding disable-llvm-passes option to make the test more stable as well. Differential Revision: https://reviews.llvm.org/D89269 Added: clang/test/CodeGenCoroutines/coro-symmetric-transfer.cpp Modified: Removed: clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp diff --git a/clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp b/clang/test/CodeGenCoroutines/coro-symmetric-transfer.cpp similarity index 58% rename from clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp rename to clang/test/CodeGenCoroutines/coro-symmetric-transfer.cpp index 9833f14b273d..4f841a918bcf 100644 --- a/clang/test/CodeGenCoroutines/coro-semmetric-transfer.cpp +++ b/clang/test/CodeGenCoroutines/coro-symmetric-transfer.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -std=c++14 -fcoroutines-ts -emit-llvm -S -O1 %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -O1 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s #include "Inputs/coroutine.h" @@ -48,6 +48,10 @@ detached_task foo() { co_return; } -// check that the lifetime of the coroutine handle used to obtain the address ended right away. -// CHECK: %{{.*}} = call i8* @{{.*address.*}}(%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* nonnull %{{.*}}) -// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %{{.*}}) +// check that the lifetime of the coroutine handle used to obtain the address is contained within single basic block. +// CHECK-LABEL: final.suspend: +// CHECK: %[[PTR1:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[ADDR_TMP:.+]] to i8* +// CHECK-NEXT:call void @llvm.lifetime.start.p0i8(i64 8, i8* %[[PTR1]]) +// CHECK: call i8* @{{.*address.*}}(%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[ADDR_TMP]]) +// CHECK-NEXT:%[[PTR2:.+]] = bitcast %"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[ADDR_TMP]] to i8* +// CHECK-NEXT:call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[PTR2]]) ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 31eb696 - [Coroutines] Remove CoroElide from O0 pipeline
Author: Xun Li Date: 2021-06-28T19:28:27-07:00 New Revision: 31eb696fc4cd3b1ed8054d88af54f214c0f92989 URL: https://github.com/llvm/llvm-project/commit/31eb696fc4cd3b1ed8054d88af54f214c0f92989 DIFF: https://github.com/llvm/llvm-project/commit/31eb696fc4cd3b1ed8054d88af54f214c0f92989.diff LOG: [Coroutines] Remove CoroElide from O0 pipeline CoroElide pass works only when a post-split coroutine is inlined into another post-split coroutine. In O0, there is no inlining after CoroSplit, and hence no CoroElide can happen. It's useless to put CoroElide pass in the O0 pipeline and it will never be triggered (unless I miss anything). Differential Revision: https://reviews.llvm.org/D105066 Added: Modified: clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp llvm/lib/Passes/PassBuilder.cpp llvm/test/Transforms/Coroutines/smoketest.ll Removed: diff --git a/clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp b/clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp index 83f8121296690..91e0fb3042b9d 100644 --- a/clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp +++ b/clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp @@ -3,23 +3,23 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm-bc -o /dev/null \ // RUN: -fexperimental-new-pass-manager -fdebug-pass-manager -fcoroutines-ts \ -// RUN: -O0 %s 2>&1 | FileCheck %s +// RUN: -O0 %s 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm-bc -o /dev/null \ // RUN: -fexperimental-new-pass-manager -fdebug-pass-manager -fcoroutines-ts \ -// RUN: -O1 %s 2>&1 | FileCheck %s +// RUN: -O1 %s 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-OPT // -// CHECK: Running pass:{{.*}}CoroEarlyPass +// CHECK-ALL: Running pass:{{.*}}CoroEarlyPass // // The first coro-split pass enqueues a second run of the entire CGSCC pipeline. -// CHECK: Running pass: CoroSplitPass on (_Z3foov) -// CHECK: Running pass:{{.*}}CoroElidePass{{.*}} on {{.*}}_Z3foov{{.*}} +// CHECK-ALL: Running pass: CoroSplitPass on (_Z3foov) +// CHECK-OPT: Running pass:{{.*}}CoroElidePass{{.*}} on {{.*}}_Z3foov{{.*}} // // The second coro-split pass splits coroutine 'foo' into funclets // 'foo.resume', 'foo.destroy', and 'foo.cleanup'. -// CHECK: Running pass: CoroSplitPass on (_Z3foov) -// CHECK: Running pass:{{.*}}CoroElidePass{{.*}} on {{.*}}_Z3foov{{.*}} +// CHECK-ALL: Running pass: CoroSplitPass on (_Z3foov) +// CHECK-OPT: Running pass:{{.*}}CoroElidePass{{.*}} on {{.*}}_Z3foov{{.*}} // -// CHECK: Running pass:{{.*}}CoroCleanupPass +// CHECK-ALL: Running pass:{{.*}}CoroCleanupPass namespace std { namespace experimental { diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 49f6c1049625f..2db8b451bf16d 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -1986,7 +1986,6 @@ ModulePassManager PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level, CGSCCPassManager CGPM; CGPM.addPass(CoroSplitPass()); -CGPM.addPass(createCGSCCToFunctionPassAdaptor(CoroElidePass())); MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); MPM.addPass(createModuleToFunctionPassAdaptor(CoroCleanupPass())); diff --git a/llvm/test/Transforms/Coroutines/smoketest.ll b/llvm/test/Transforms/Coroutines/smoketest.ll index bb8d26783ca9d..bd122ff00180c 100644 --- a/llvm/test/Transforms/Coroutines/smoketest.ll +++ b/llvm/test/Transforms/Coroutines/smoketest.ll @@ -2,21 +2,21 @@ ; levels and -enable-coroutines adds coroutine passes to the pipeline. ; ; RUN: opt < %s -disable-output -passes='default' -enable-coroutines \ -; RUN: -debug-pass-manager 2>&1 | FileCheck %s +; RUN: -debug-pass-manager 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL ; RUN: opt < %s -disable-output -passes='default' -enable-coroutines \ -; RUN: -debug-pass-manager 2>&1 | FileCheck %s +; RUN: -debug-pass-manager 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-OPT ; RUN: opt < %s -disable-output -passes='default' -enable-coroutines \ -; RUN: -debug-pass-manager 2>&1 | FileCheck %s +; RUN: -debug-pass-manager 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-OPT ; RUN: opt < %s -disable-output -passes='default' -enable-coroutines \ -; RUN: -debug-pass-manager 2>&1 | FileCheck %s +; RUN: -debug-pass-manager 2>&1 | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-OPT ; RUN: opt < %s -disable-output -debug-pass-manager \ ; RUN: -passes='function(coro-early),cgscc(coro-split),function(coro-elide,coro-cleanup)' 2>&1 \ -; RUN: | FileCheck %s +; RUN: | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-OPT -; CHECK: CoroEarlyPass -; CHECK: CoroSplitPass -; CHECK: CoroElidePass -; CHECK: CoroCleanupPass +; CHECK-ALL: CoroEarlyPass +; CHECK-ALL: CoroSplitPass +; CHECK-OPT: CoroElidePass +; CHE
[clang] 822b92a - [Coroutines] Add the newly generated SCCs back to the CGSCC work queue after CoroSplit actually happened
Author: Xun Li Date: 2021-06-30T11:38:14-07:00 New Revision: 822b92aae439c4ba2946980c8a27bd2c8a62d90c URL: https://github.com/llvm/llvm-project/commit/822b92aae439c4ba2946980c8a27bd2c8a62d90c DIFF: https://github.com/llvm/llvm-project/commit/822b92aae439c4ba2946980c8a27bd2c8a62d90c.diff LOG: [Coroutines] Add the newly generated SCCs back to the CGSCC work queue after CoroSplit actually happened Relevant discussion can be found at: https://lists.llvm.org/pipermail/llvm-dev/2021-January/148197.html In the existing design, An SCC that contains a coroutine will go through the folloing passes: Inliner -> CoroSplitPass (fake) -> FunctionSimplificationPipeline -> Inliner -> CoroSplitPass (real) -> FunctionSimplificationPipeline The first CoroSplitPass doesn't do anything other than putting the SCC back to the queue so that the entire pipeline can repeat. As you can see, we run Inliner twice on the SCC consecutively without doing any real split, which is unnecessary and likely unintended. What we really wanted is this: Inliner -> FunctionSimplificationPipeline -> CoroSplitPass -> FunctionSimplificationPipeline (note that we don't really need to run Inliner again on the ramp function after split). Hence the way we do it here is to move CoroSplitPass to the end of the CGSCC pipeline, make it once for real, insert the newly generated SCCs (the clones) back to the pipeline so that they can be optimized, and also add a function simplification pipeline after CoroSplit to optimize the post-split ramp function. This approach also conforms to how the new pass manager works instead of relying on an adhoc post split cleanup, making it ready for full switch to new pass manager eventually. By looking at some of the changes to the tests, we can already observe that this changes allows for more optimizations applied to coroutines. Reviewed By: aeubanks, ChuanqiXu Differential Revision: https://reviews.llvm.org/D95807 Added: Modified: clang/test/CodeGenCoroutines/coro-newpm-pipeline.cpp llvm/lib/Passes/PassBuilder.cpp llvm/lib/Transforms/Coroutines/CoroSplit.cpp llvm/test/Transforms/Coroutines/ArgAddr.ll llvm/test/Transforms/Coroutines/coro-alloc-with-param-O0.ll llvm/test/Transforms/Coroutines/coro-alloc-with-param-O2.ll llvm/test/Transforms/Coroutines/coro-alloca-01.ll llvm/test/Transforms/Coroutines/coro-alloca-02.ll llvm/test/Transforms/Coroutines/coro-alloca-03.ll llvm/test/Transforms/Coroutines/coro-alloca-04.ll llvm/test/Transforms/Coroutines/coro-alloca-05.ll llvm/test/Transforms/Coroutines/coro-alloca-06.ll llvm/test/Transforms/Coroutines/coro-alloca-07.ll llvm/test/Transforms/Coroutines/coro-alloca-08.ll llvm/test/Transforms/Coroutines/coro-async.ll llvm/test/Transforms/Coroutines/coro-byval-param.ll llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll llvm/test/Transforms/Coroutines/coro-catchswitch.ll llvm/test/Transforms/Coroutines/coro-debug.ll llvm/test/Transforms/Coroutines/coro-eh-aware-edge-split-00.ll llvm/test/Transforms/Coroutines/coro-eh-aware-edge-split-01.ll llvm/test/Transforms/Coroutines/coro-eh-aware-edge-split-02.ll llvm/test/Transforms/Coroutines/coro-frame-arrayalloca.ll llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-00.ll llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-01.ll llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-02.ll llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-03.ll llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-04.ll llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-05.ll llvm/test/Transforms/Coroutines/coro-frame-unreachable.ll llvm/test/Transforms/Coroutines/coro-frame.ll llvm/test/Transforms/Coroutines/coro-materialize.ll llvm/test/Transforms/Coroutines/coro-padding.ll llvm/test/Transforms/Coroutines/coro-param-copy.ll llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll llvm/test/Transforms/Coroutines/coro-retcon-frame.ll llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll llvm/test/Transforms/Coroutines/coro-retcon-unreachable.ll llvm/test/Transforms/Coroutines/coro-retcon-value.ll llvm/test/Transforms/Coroutines/coro-retcon.ll llvm/test/Transforms/Coroutines/coro-spill-after-phi.ll llvm/test/Transforms/Coroutines/coro-spill-corobegin.ll llvm/test/Transforms/Coroutines/coro-spill-defs-before-corobegin.ll llvm/test/Transforms/Coroutines/coro-spill-promise.ll llvm/test/Transforms/Coroutines/coro-split-00.ll llvm/test/Transforms/Coroutines/coro-split-02.ll llvm/test/Transforms/Coroutines/coro-split-alloc.ll llvm/test/Transforms/Coroutines/coro-split-dbg.ll llvm/test/Transforms/Coroutines/coro-split-e
[clang] c7a39c8 - [Coroutine][Clang] Force emit lifetime intrinsics for Coroutines
Author: Xun Li Date: 2021-03-25T13:46:20-07:00 New Revision: c7a39c833af173a7797692757b13b4b8eb801acd URL: https://github.com/llvm/llvm-project/commit/c7a39c833af173a7797692757b13b4b8eb801acd DIFF: https://github.com/llvm/llvm-project/commit/c7a39c833af173a7797692757b13b4b8eb801acd.diff LOG: [Coroutine][Clang] Force emit lifetime intrinsics for Coroutines tl;dr Correct implementation of Corouintes requires having lifetime intrinsics available. Coroutine functions are functions that can be suspended and resumed latter. To do so, data that need to stay alive after suspension must be put on the heap (i.e. the coroutine frame). The optimizer is responsible for analyzing each AllocaInst and figure out whether it should be put on the stack or the frame. In most cases, for data that we are unable to accurately analyze lifetime, we can just conservatively put them on the heap. Unfortunately, there exists a few cases where certain data MUST be put on the stack, not on the heap. Without lifetime intrinsics, we are unable to correctly analyze those data's lifetime. To dig into more details, there exists cases where at certain code points, the current coroutine frame may have already been destroyed. Hence no frame access would be allowed beyond that point. The following is a common code pattern called "Symmetric Transfer" in coroutine: ``` auto tmp = await_suspend(); __builtin_coro_resume(tmp.address()); return; ``` In the above code example, `await_suspend()` returns a new coroutine handle, which we will obtain the address and then resume that coroutine. This essentially "transfered" from the current coroutine to a different coroutine. During the call to `await_suspend()`, the current coroutine may be destroyed, which should be fine because we are not accessing any data afterwards. However when LLVM is emitting IR for the above code, it needs to emit an AllocaInst for `tmp`. It will then call the `address` function on tmp. `address` function is a member function of coroutine, and there is no way for the LLVM optimizer to know that it does not capture the `tmp` pointer. So when the optimizer looks at it, it has to conservatively assume that `tmp` may escape and hence put it on the heap. Furthermore, in some cases `address` call would be inlined, which will generate a bunch of store/load instructions that move the `tmp` pointer around. Those stores will also make the compiler to think that `tmp` might escape. To summarize, it's really difficult for the mid-end to figure out that the `tmp` data is short-lived. I made some attempt in D98638, but it appears to be way too complex and is basically doing the same thing as inserting lifetime intrinsics in coroutines. Also, for reference, we already force emitting lifetime intrinsics in O0 for AlwaysInliner: https://github.com/llvm/llvm-project/blob/main/llvm/lib/Passes/PassBuilder.cpp#L1893 Differential Revision: https://reviews.llvm.org/D99227 Added: Modified: clang/lib/CodeGen/CGCoroutine.cpp clang/lib/CodeGen/CodeGenFunction.cpp clang/lib/CodeGen/CodeGenFunction.h clang/test/CodeGenCoroutines/coro-alloc.cpp clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp clang/test/CodeGenCoroutines/coro-await.cpp clang/test/CodeGenCoroutines/coro-dest-slot.cpp clang/test/CodeGenCoroutines/coro-params.cpp clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp Removed: diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index 5c57ad0685d5..038238c84046 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -556,6 +556,8 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { {Builder.getInt32(NewAlign), NullPtr, NullPtr, NullPtr}); createCoroData(*this, CurCoro, CoroId); CurCoro.Data->SuspendBB = RetBB; + assert(ShouldEmitLifetimeMarkers && + "Must emit lifetime intrinsics for coroutines"); // Backend is allowed to elide memory allocations, to help it, emit // auto mem = coro.alloc() ? 0 : ... allocation code ...; diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index e3fdf54716ab..600312e15ef4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1318,10 +1318,16 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, Stmt *Body = FD->getBody(); - // Initialize helper which will detect jumps which can cause invalid lifetime - // markers. - if (Body && ShouldEmitLifetimeMarkers) -Bypasses.Init(Body); + if (Body) { +// Coroutines always emit lifetime markers. +if (isa(Body)) + ShouldEmitLifetimeMarkers = true; + +// Initialize helper which will detect jumps which can cause invalid +// lifetime marke
[clang] f490a59 - [OpenMP][InstrProfiling] Fix a missing instr profiling counter
Author: Xun Li Date: 2021-03-25T13:52:36-07:00 New Revision: f490a5969bd52c8a48586f134ff8f02ccbb295b3 URL: https://github.com/llvm/llvm-project/commit/f490a5969bd52c8a48586f134ff8f02ccbb295b3 DIFF: https://github.com/llvm/llvm-project/commit/f490a5969bd52c8a48586f134ff8f02ccbb295b3.diff LOG: [OpenMP][InstrProfiling] Fix a missing instr profiling counter When emitting a function body there needs to be a instr profiling counter emitted. Otherwise instr profiling won't work for this function. Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D98135 Added: clang/test/OpenMP/omp_with_loop_pragma_instr_profile.c Modified: clang/lib/CodeGen/CGOpenMPRuntime.cpp Removed: diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp index a8f21548d3e0..466ff096b585 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -1034,7 +1034,7 @@ LValue CGOpenMPRegionInfo::getThreadIDVariableLValue(CodeGenFunction &CGF) { getThreadIDVariable()->getType()->castAs()); } -void CGOpenMPRegionInfo::EmitBody(CodeGenFunction &CGF, const Stmt * /*S*/) { +void CGOpenMPRegionInfo::EmitBody(CodeGenFunction &CGF, const Stmt *S) { if (!CGF.HaveInsertPoint()) return; // 1.2.2 OpenMP Language Terminology @@ -1043,6 +1043,8 @@ void CGOpenMPRegionInfo::EmitBody(CodeGenFunction &CGF, const Stmt * /*S*/) { // The point of exit cannot be a branch out of the structured block. // longjmp() and throw() must not violate the entry/exit criteria. CGF.EHStack.pushTerminate(); + if (S) +CGF.incrementProfileCounter(S); CodeGen(CGF); CGF.EHStack.popTerminate(); } diff --git a/clang/test/OpenMP/omp_with_loop_pragma_instr_profile.c b/clang/test/OpenMP/omp_with_loop_pragma_instr_profile.c new file mode 100644 index ..9667f9cc549d --- /dev/null +++ b/clang/test/OpenMP/omp_with_loop_pragma_instr_profile.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c -emit-llvm %s -triple x86_64-unknown-linux -o - -femit-all-decls -disable-llvm-passes -fprofile-instrument=clang | FileCheck %s +// expected-no-diagnostics + +void sub(double *restrict a, double *restrict b, int n) { + int i; + +#pragma omp parallel for +#pragma clang loop vectorize(disable) + for (i = 0; i < n; i++) { +a[i] = a[i] + b[i]; + } +} + +// CHECK-LABEL: @.omp_outlined.( +// CHECK-NEXT: entry: +// CHECK: call void @llvm.instrprof.increment( +// CHECK: omp.precond.then: +// CHECK-NEXT:call void @llvm.instrprof.increment( +// CHECK: cond.true: +// CEHCK-NEXT:call void @llvm.instrprof.increment( +// CHECK: omp.inner.for.body: +// CHECK-NEXT:call void @llvm.instrprof.increment( ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 2b50f5a - [Coroutines] Move CoroEarly pass to before AlwaysInliner
Author: Xun Li Date: 2021-04-18T14:54:04-07:00 New Revision: 2b50f5a4343f8fb06acaa5c36355bcf58092c9cd URL: https://github.com/llvm/llvm-project/commit/2b50f5a4343f8fb06acaa5c36355bcf58092c9cd DIFF: https://github.com/llvm/llvm-project/commit/2b50f5a4343f8fb06acaa5c36355bcf58092c9cd.diff LOG: [Coroutines] Move CoroEarly pass to before AlwaysInliner Presplit coroutines cannot be inlined. During AlwaysInliner we check if a function is a presplit coroutine, if so we skip inlining. The presplit coroutine attributes are set in CoroEarly pass. However in O0 pipeline, AlwaysInliner runs before CoroEarly, so the attribute isn't set yet and will still inline the coroutine. This causes Clang to crash: https://bugs.llvm.org/show_bug.cgi?id=49920 Differential Revision: https://reviews.llvm.org/D100282 Added: clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp Modified: clang/lib/CodeGen/CGCoroutine.cpp clang/test/CodeGenCoroutines/coro-always-inline.cpp llvm/lib/Transforms/Coroutines/CoroEarly.cpp llvm/test/Transforms/Coroutines/coro-debug-O2.ll llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll llvm/test/Transforms/Coroutines/coro-split-01.ll llvm/test/Transforms/Coroutines/coro-split-recursive.ll llvm/test/Transforms/Coroutines/ex0.ll llvm/test/Transforms/Coroutines/ex1.ll llvm/test/Transforms/Coroutines/ex2.ll llvm/test/Transforms/Coroutines/ex3.ll llvm/test/Transforms/Coroutines/ex4.ll llvm/test/Transforms/Coroutines/ex5.ll llvm/test/Transforms/Coroutines/phi-coro-end.ll llvm/test/Transforms/Coroutines/restart-trigger.ll Removed: diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index ca071d3d2e80f..fcf8fe062367f 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -558,6 +558,8 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { CurCoro.Data->SuspendBB = RetBB; assert(ShouldEmitLifetimeMarkers && "Must emit lifetime intrinsics for coroutines"); + // CORO_PRESPLIT_ATTR = UNPREPARED_FOR_SPLIT + CurFn->addFnAttr("coroutine.presplit", "0"); // Backend is allowed to elide memory allocations, to help it, emit // auto mem = coro.alloc() ? 0 : ... allocation code ...; diff --git a/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp b/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp new file mode 100644 index 0..e4aa14a6ac397 --- /dev/null +++ b/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ +// RUN: -fexperimental-new-pass-manager -O0 %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ +// RUN: -fexperimental-new-pass-manager -fno-inline -O0 %s -o - | FileCheck %s + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ +// RUN: -O0 %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ +// RUN: -fno-inline -O0 %s -o - | FileCheck %s + +namespace std { +namespace experimental { + +struct handle {}; + +struct awaitable { + bool await_ready() noexcept { return true; } + // CHECK-NOT: await_suspend + inline void __attribute__((__always_inline__)) await_suspend(handle) noexcept {} + bool await_resume() noexcept { return true; } +}; + +template +struct coroutine_handle { + static handle from_address(void *address) noexcept { return {}; } +}; + +template +struct coroutine_traits { + struct promise_type { +awaitable initial_suspend() { return {}; } +awaitable final_suspend() noexcept { return {}; } +void return_void() {} +T get_return_object() { return T(); } +void unhandled_exception() {} + }; +}; +} // namespace experimental +} // namespace std + +// CHECK-LABEL: @_Z3foov +// CHECK-LABEL: entry: +// CHECK-NEXT: %this.addr.i{{[0-9]*}} = alloca %"struct.std::experimental::awaitable"*, align 8 +// CHECK-NEXT: %this.addr.i{{[0-9]*}} = alloca %"struct.std::experimental::awaitable"*, align 8 +// CHECK: [[CAST0:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* +// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[CAST0]]) +// CHECK: [[CAST1:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* +// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[CAST1]]) + +// CHECK: [[CAST2:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* +// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[CAST2]]) +// CHECK: [[CAST3:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* +// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[CAST3]]) +void f
[clang] c0211e8 - Revert "[Coroutines] Move CoroEarly pass to before AlwaysInliner"
Author: Xun Li Date: 2021-04-18T15:38:19-07:00 New Revision: c0211e8d7d0b797fd11543c3d3f9fecf3b2069cf URL: https://github.com/llvm/llvm-project/commit/c0211e8d7d0b797fd11543c3d3f9fecf3b2069cf DIFF: https://github.com/llvm/llvm-project/commit/c0211e8d7d0b797fd11543c3d3f9fecf3b2069cf.diff LOG: Revert "[Coroutines] Move CoroEarly pass to before AlwaysInliner" This reverts commit 2b50f5a4343f8fb06acaa5c36355bcf58092c9cd. Forgot to update the description of the commit to sync with phabricator. Going to redo the commit. Added: Modified: clang/lib/CodeGen/CGCoroutine.cpp clang/test/CodeGenCoroutines/coro-always-inline.cpp llvm/lib/Transforms/Coroutines/CoroEarly.cpp llvm/test/Transforms/Coroutines/coro-debug-O2.ll llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll llvm/test/Transforms/Coroutines/coro-split-01.ll llvm/test/Transforms/Coroutines/coro-split-recursive.ll llvm/test/Transforms/Coroutines/ex0.ll llvm/test/Transforms/Coroutines/ex1.ll llvm/test/Transforms/Coroutines/ex2.ll llvm/test/Transforms/Coroutines/ex3.ll llvm/test/Transforms/Coroutines/ex4.ll llvm/test/Transforms/Coroutines/ex5.ll llvm/test/Transforms/Coroutines/phi-coro-end.ll llvm/test/Transforms/Coroutines/restart-trigger.ll Removed: clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index fcf8fe062367f..ca071d3d2e80f 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -558,8 +558,6 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { CurCoro.Data->SuspendBB = RetBB; assert(ShouldEmitLifetimeMarkers && "Must emit lifetime intrinsics for coroutines"); - // CORO_PRESPLIT_ATTR = UNPREPARED_FOR_SPLIT - CurFn->addFnAttr("coroutine.presplit", "0"); // Backend is allowed to elide memory allocations, to help it, emit // auto mem = coro.alloc() ? 0 : ... allocation code ...; diff --git a/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp b/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp deleted file mode 100644 index e4aa14a6ac397..0 --- a/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ -// RUN: -fexperimental-new-pass-manager -O0 %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ -// RUN: -fexperimental-new-pass-manager -fno-inline -O0 %s -o - | FileCheck %s - -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ -// RUN: -O0 %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ -// RUN: -fno-inline -O0 %s -o - | FileCheck %s - -namespace std { -namespace experimental { - -struct handle {}; - -struct awaitable { - bool await_ready() noexcept { return true; } - // CHECK-NOT: await_suspend - inline void __attribute__((__always_inline__)) await_suspend(handle) noexcept {} - bool await_resume() noexcept { return true; } -}; - -template -struct coroutine_handle { - static handle from_address(void *address) noexcept { return {}; } -}; - -template -struct coroutine_traits { - struct promise_type { -awaitable initial_suspend() { return {}; } -awaitable final_suspend() noexcept { return {}; } -void return_void() {} -T get_return_object() { return T(); } -void unhandled_exception() {} - }; -}; -} // namespace experimental -} // namespace std - -// CHECK-LABEL: @_Z3foov -// CHECK-LABEL: entry: -// CHECK-NEXT: %this.addr.i{{[0-9]*}} = alloca %"struct.std::experimental::awaitable"*, align 8 -// CHECK-NEXT: %this.addr.i{{[0-9]*}} = alloca %"struct.std::experimental::awaitable"*, align 8 -// CHECK: [[CAST0:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* -// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[CAST0]]) -// CHECK: [[CAST1:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* -// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[CAST1]]) - -// CHECK: [[CAST2:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* -// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[CAST2]]) -// CHECK: [[CAST3:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* -// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[CAST3]]) -void foo() { co_return; } diff --git a/clang/test/CodeGenCoroutines/coro-always-inline.cpp b/clang/test/CodeGenCoroutines/coro-always-inline.cpp index 6ba5a6f124169..e4aa14a6ac397 100644 --- a/clang/test/CodeGenCoroutines/coro-always-inline.cpp +++ b/clang/test/CodeGenCoroutines/c
[clang] fa6b54c - [Coroutines] Set presplit attribute in Clang instead of CoroEarly pass
Author: Xun Li Date: 2021-04-18T15:41:09-07:00 New Revision: fa6b54c44ab1d5f579304eadb7ac8bd7e72d0e77 URL: https://github.com/llvm/llvm-project/commit/fa6b54c44ab1d5f579304eadb7ac8bd7e72d0e77 DIFF: https://github.com/llvm/llvm-project/commit/fa6b54c44ab1d5f579304eadb7ac8bd7e72d0e77.diff LOG: [Coroutines] Set presplit attribute in Clang instead of CoroEarly pass Presplit coroutines cannot be inlined. During AlwaysInliner we check if a function is a presplit coroutine, if so we skip inlining. The presplit coroutine attributes are set in CoroEarly pass. However in O0 pipeline, AlwaysInliner runs before CoroEarly, so the attribute isn't set yet and will still inline the coroutine. This causes Clang to crash: https://bugs.llvm.org/show_bug.cgi?id=49920 To fix this, we set the attributes in the Clang front-end instead of in CoroEarly pass. Reviewed By: rjmccall, ChuanqiXu Differential Revision: https://reviews.llvm.org/D100282 Added: clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp Modified: clang/lib/CodeGen/CGCoroutine.cpp clang/test/CodeGenCoroutines/coro-always-inline.cpp llvm/lib/Transforms/Coroutines/CoroEarly.cpp llvm/test/Transforms/Coroutines/coro-debug-O2.ll llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll llvm/test/Transforms/Coroutines/coro-split-01.ll llvm/test/Transforms/Coroutines/coro-split-recursive.ll llvm/test/Transforms/Coroutines/ex0.ll llvm/test/Transforms/Coroutines/ex1.ll llvm/test/Transforms/Coroutines/ex2.ll llvm/test/Transforms/Coroutines/ex3.ll llvm/test/Transforms/Coroutines/ex4.ll llvm/test/Transforms/Coroutines/ex5.ll llvm/test/Transforms/Coroutines/phi-coro-end.ll llvm/test/Transforms/Coroutines/restart-trigger.ll Removed: diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index ca071d3d2e80f..fcf8fe062367f 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -558,6 +558,8 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { CurCoro.Data->SuspendBB = RetBB; assert(ShouldEmitLifetimeMarkers && "Must emit lifetime intrinsics for coroutines"); + // CORO_PRESPLIT_ATTR = UNPREPARED_FOR_SPLIT + CurFn->addFnAttr("coroutine.presplit", "0"); // Backend is allowed to elide memory allocations, to help it, emit // auto mem = coro.alloc() ? 0 : ... allocation code ...; diff --git a/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp b/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp new file mode 100644 index 0..e4aa14a6ac397 --- /dev/null +++ b/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ +// RUN: -fexperimental-new-pass-manager -O0 %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ +// RUN: -fexperimental-new-pass-manager -fno-inline -O0 %s -o - | FileCheck %s + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ +// RUN: -O0 %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ +// RUN: -fno-inline -O0 %s -o - | FileCheck %s + +namespace std { +namespace experimental { + +struct handle {}; + +struct awaitable { + bool await_ready() noexcept { return true; } + // CHECK-NOT: await_suspend + inline void __attribute__((__always_inline__)) await_suspend(handle) noexcept {} + bool await_resume() noexcept { return true; } +}; + +template +struct coroutine_handle { + static handle from_address(void *address) noexcept { return {}; } +}; + +template +struct coroutine_traits { + struct promise_type { +awaitable initial_suspend() { return {}; } +awaitable final_suspend() noexcept { return {}; } +void return_void() {} +T get_return_object() { return T(); } +void unhandled_exception() {} + }; +}; +} // namespace experimental +} // namespace std + +// CHECK-LABEL: @_Z3foov +// CHECK-LABEL: entry: +// CHECK-NEXT: %this.addr.i{{[0-9]*}} = alloca %"struct.std::experimental::awaitable"*, align 8 +// CHECK-NEXT: %this.addr.i{{[0-9]*}} = alloca %"struct.std::experimental::awaitable"*, align 8 +// CHECK: [[CAST0:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* +// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[CAST0]]) +// CHECK: [[CAST1:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* +// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[CAST1]]) + +// CHECK: [[CAST2:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* +// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[CAST2]]) +// CHECK: [[CAST3:%[0-9]+]] = bitcast %"struct.std:
[clang] 5faba87 - Revert "[Coroutines] Set presplit attribute in Clang instead of CoroEarly pass"
Author: Xun Li Date: 2021-04-18T17:22:28-07:00 New Revision: 5faba87938779c595f2b4e40f933bae6571bc421 URL: https://github.com/llvm/llvm-project/commit/5faba87938779c595f2b4e40f933bae6571bc421 DIFF: https://github.com/llvm/llvm-project/commit/5faba87938779c595f2b4e40f933bae6571bc421.diff LOG: Revert "[Coroutines] Set presplit attribute in Clang instead of CoroEarly pass" This reverts commit fa6b54c44ab1d5f579304eadb7ac8bd7e72d0e77. The commited patch broke mlir tests. It seems that mlir tests depend on coroutine function properties set in CoroEarly pass. Added: Modified: clang/lib/CodeGen/CGCoroutine.cpp clang/test/CodeGenCoroutines/coro-always-inline.cpp llvm/lib/Transforms/Coroutines/CoroEarly.cpp llvm/test/Transforms/Coroutines/coro-debug-O2.ll llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll llvm/test/Transforms/Coroutines/coro-split-01.ll llvm/test/Transforms/Coroutines/coro-split-recursive.ll llvm/test/Transforms/Coroutines/ex0.ll llvm/test/Transforms/Coroutines/ex1.ll llvm/test/Transforms/Coroutines/ex2.ll llvm/test/Transforms/Coroutines/ex3.ll llvm/test/Transforms/Coroutines/ex4.ll llvm/test/Transforms/Coroutines/ex5.ll llvm/test/Transforms/Coroutines/phi-coro-end.ll llvm/test/Transforms/Coroutines/restart-trigger.ll Removed: clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index fcf8fe062367f..ca071d3d2e80f 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -558,8 +558,6 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { CurCoro.Data->SuspendBB = RetBB; assert(ShouldEmitLifetimeMarkers && "Must emit lifetime intrinsics for coroutines"); - // CORO_PRESPLIT_ATTR = UNPREPARED_FOR_SPLIT - CurFn->addFnAttr("coroutine.presplit", "0"); // Backend is allowed to elide memory allocations, to help it, emit // auto mem = coro.alloc() ? 0 : ... allocation code ...; diff --git a/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp b/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp deleted file mode 100644 index e4aa14a6ac397..0 --- a/clang/test/CodeGenCoroutines/coro-always-inline-resume.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ -// RUN: -fexperimental-new-pass-manager -O0 %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ -// RUN: -fexperimental-new-pass-manager -fno-inline -O0 %s -o - | FileCheck %s - -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ -// RUN: -O0 %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcoroutines-ts \ -// RUN: -fno-inline -O0 %s -o - | FileCheck %s - -namespace std { -namespace experimental { - -struct handle {}; - -struct awaitable { - bool await_ready() noexcept { return true; } - // CHECK-NOT: await_suspend - inline void __attribute__((__always_inline__)) await_suspend(handle) noexcept {} - bool await_resume() noexcept { return true; } -}; - -template -struct coroutine_handle { - static handle from_address(void *address) noexcept { return {}; } -}; - -template -struct coroutine_traits { - struct promise_type { -awaitable initial_suspend() { return {}; } -awaitable final_suspend() noexcept { return {}; } -void return_void() {} -T get_return_object() { return T(); } -void unhandled_exception() {} - }; -}; -} // namespace experimental -} // namespace std - -// CHECK-LABEL: @_Z3foov -// CHECK-LABEL: entry: -// CHECK-NEXT: %this.addr.i{{[0-9]*}} = alloca %"struct.std::experimental::awaitable"*, align 8 -// CHECK-NEXT: %this.addr.i{{[0-9]*}} = alloca %"struct.std::experimental::awaitable"*, align 8 -// CHECK: [[CAST0:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* -// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[CAST0]]) -// CHECK: [[CAST1:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* -// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[CAST1]]) - -// CHECK: [[CAST2:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* -// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[CAST2]]) -// CHECK: [[CAST3:%[0-9]+]] = bitcast %"struct.std::experimental::awaitable"** %this.addr.i{{[0-9]*}} to i8* -// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[CAST3]]) -void foo() { co_return; } diff --git a/clang/test/CodeGenCoroutines/coro-always-inline.cpp b/clang/test/CodeGenCoroutines/coro-always-inline.cpp index 6ba5a6f124169..e4aa14a6ac397 100644 --- a/clang/test/CodeGenCoroutines/coro-always-inline