[PATCH] D82314: [RFC][Coroutines] Optimize the lifespan of temporary co_await object
Richard Smith - zygoloid via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 23 11:48:39 PDT 2020
rsmith added a comment.
In D82314#2109437 <https://reviews.llvm.org/D82314#2109437>, @lxfind wrote:
> In D82314#2107910 <https://reviews.llvm.org/D82314#2107910>, @junparser wrote:
>
> > Rather than doing it here, can we build await_resume call expression with MaterializedTemporaryExpr when expand the coawait expression. That's how gcc does.
>
>
> There doesn't appear to be a way to do that in Clang. It goes from the AST to IR directly, and there needs to be a MaterializedTemporaryExpr to wrap the result of co_await. Could you elaborate on how this might be done in Clang?
For a call such as:
coro f() { awaitable a; (co_await a).g(); }
we produce an AST like:
| | `-CXXMemberCallExpr <col:25, col:40> 'void'
| | `-MemberExpr <col:25, col:38> '<bound member function type>' .g 0x55d38948ca98
| | `-MaterializeTemporaryExpr <col:25, col:36> 'huge' xvalue
| | `-ParenExpr <col:25, col:36> 'huge'
| | `-CoawaitExpr <col:26, col:35> 'huge'
| | |-DeclRefExpr <col:35> 'awaitable' lvalue Var 0x55d38948d4c8 'a' 'awaitable'
| | |-CXXMemberCallExpr <col:35> 'bool'
| | | `-MemberExpr <col:35> '<bound member function type>' .await_ready 0x55d38948cee8
| | | `-OpaqueValueExpr <col:35> 'awaitable' lvalue
| | | `-DeclRefExpr <col:35> 'awaitable' lvalue Var 0x55d38948d4c8 'a' 'awaitable'
| | |-CXXMemberCallExpr <col:35> 'void'
| | | |-MemberExpr <col:35> '<bound member function type>' .await_suspend 0x55d38948d298
| | | | `-OpaqueValueExpr <col:35> 'awaitable' lvalue
| | | | `-DeclRefExpr <col:35> 'awaitable' lvalue Var 0x55d38948d4c8 'a' 'awaitable'
| | | `-CXXConstructExpr <col:35> 'std::coroutine_handle<>':'std::experimental::coroutines_v1::coroutine_handle<void>' 'void (std::experimental::coroutines_v1::coroutine_handle<void> &&) noexcept'
| | | `-ImplicitCastExpr <col:35> 'std::experimental::coroutines_v1::coroutine_handle<void>' xvalue <DerivedToBase (coroutine_handle)>
| | | `-MaterializeTemporaryExpr <col:35> 'std::experimental::coroutines_v1::coroutine_handle<coro::promise_type>' xvalue
| | | `-CallExpr <col:35> 'std::experimental::coroutines_v1::coroutine_handle<coro::promise_type>'
| | | |-ImplicitCastExpr <col:35> 'std::experimental::coroutines_v1::coroutine_handle<coro::promise_type> (*)(void *) noexcept' <FunctionToPointerDecay>
| | | | `-DeclRefExpr <col:35> 'std::experimental::coroutines_v1::coroutine_handle<coro::promise_type> (void *) noexcept' lvalue CXXMethod 0x55d38948adb0 'from_address' 'std::experimental::coroutines_v1::coroutine_handle<coro::promise_type> (void *) noexcept'
| | | `-CallExpr <col:35> 'void *'
| | | `-ImplicitCastExpr <col:35> 'void *(*)() noexcept' <FunctionToPointerDecay>
| | | `-DeclRefExpr <col:35> 'void *() noexcept' lvalue Function 0x55d38948ef38 '__builtin_coro_frame' 'void *() noexcept'
| | `-CXXBindTemporaryExpr <col:35> 'huge' (CXXTemporary 0x55d38948ffb8)
| | `-CXXMemberCallExpr <col:35> 'huge'
| | `-MemberExpr <col:35> '<bound member function type>' .await_resume 0x55d38948cff8
| | `-OpaqueValueExpr <col:35> 'awaitable' lvalue
| | `-DeclRefExpr <col:35> 'awaitable' lvalue Var 0x55d38948d4c8 'a' 'awaitable'
See https://godbolt.org/z/hVn2u9.
I think the suggestion is to move the `MaterializeTemporaryExpr` from being wrapped around the `CoawaitExpr` to being wrapped around the `.await_resume` call within it.
Unfortunately, that's not a correct change. The language rules require us to delay materializing the temporary until we see how it is used. For example, in a case such as
g(co_await a);
... no temporary is materialized at all, and the `await_resume` call instead directly initializes the parameter slot for the function.
----
It seems to me that the same issue (of making large objects unnecessarily live across suspend points) can arise for other similar cases too. For example, consider:
huge(co_await a).g(); // suppose `co_await a` returns something small
Here again we will start the lifetime of the `huge` temporary before the suspend point, and only initialize it after the resume. Your change won't help here, because it's not the lifetime markers of the value produced by `co_await` that are the problem.
As a result of the above, I think this is the wrong level at which to perform this optimization. Instead, I think you should consider whether we can move lifetime start markers later (and end markers earlier, for unescaped locals) as part of the coroutine splitting pass.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D82314/new/
https://reviews.llvm.org/D82314
More information about the cfe-commits
mailing list