[all-commits] [llvm/llvm-project] e17a39: [Clang] C++20 Coroutines: Introduce Frontend Attri...
Yuxuan Chen via All-commits
all-commits at lists.llvm.org
Sun Sep 8 23:09:20 PDT 2024
Branch: refs/heads/main
Home: https://github.com/llvm/llvm-project
Commit: e17a39bc314f97231e440c9e68d9f46a9c07af6d
https://github.com/llvm/llvm-project/commit/e17a39bc314f97231e440c9e68d9f46a9c07af6d
Author: Yuxuan Chen <ych at fb.com>
Date: 2024-09-08 (Sun, 08 Sep 2024)
Changed paths:
M clang/docs/ReleaseNotes.rst
M clang/include/clang/AST/Expr.h
M clang/include/clang/AST/Stmt.h
M clang/include/clang/Basic/Attr.td
M clang/include/clang/Basic/AttrDocs.td
M clang/lib/AST/Expr.cpp
M clang/lib/CodeGen/CGBlocks.cpp
M clang/lib/CodeGen/CGCUDARuntime.cpp
M clang/lib/CodeGen/CGCUDARuntime.h
M clang/lib/CodeGen/CGCXXABI.h
M clang/lib/CodeGen/CGClass.cpp
M clang/lib/CodeGen/CGExpr.cpp
M clang/lib/CodeGen/CGExprCXX.cpp
M clang/lib/CodeGen/CodeGenFunction.h
M clang/lib/CodeGen/ItaniumCXXABI.cpp
M clang/lib/CodeGen/MicrosoftCXXABI.cpp
M clang/lib/Sema/SemaCoroutine.cpp
A clang/test/CodeGenCoroutines/Inputs/utility.h
A clang/test/CodeGenCoroutines/coro-await-elidable.cpp
M clang/test/Misc/pragma-attribute-supported-attributes-list.test
M llvm/include/llvm/Bitcode/LLVMBitCodes.h
M llvm/include/llvm/IR/Attributes.td
M llvm/lib/Bitcode/Reader/BitcodeReader.cpp
M llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
M llvm/lib/Transforms/Utils/CodeExtractor.cpp
Log Message:
-----------
[Clang] C++20 Coroutines: Introduce Frontend Attribute [[clang::coro_await_elidable]] (#99282)
This patch is the frontend implementation of the coroutine elide
improvement project detailed in this discourse post:
https://discourse.llvm.org/t/language-extension-for-better-more-deterministic-halo-for-c-coroutines/80044
This patch proposes a C++ struct/class attribute
`[[clang::coro_await_elidable]]`. This notion of await elidable task
gives developers and library authors a certainty that coroutine heap
elision happens in a predictable way.
Originally, after we lower a coroutine to LLVM IR, CoroElide is
responsible for analysis of whether an elision can happen. Take this as
an example:
```
Task foo();
Task bar() {
co_await foo();
}
```
For CoroElide to happen, the ramp function of `foo` must be inlined into
`bar`. This inlining happens after `foo` has been split but `bar` is
usually still a presplit coroutine. If `foo` is indeed a coroutine, the
inlined `coro.id` intrinsics of `foo` is visible within `bar`. CoroElide
then runs an analysis to figure out whether the SSA value of
`coro.begin()` of `foo` gets destroyed before `bar` terminates.
`Task` types are rarely simple enough for the destroy logic of the task
to reference the SSA value from `coro.begin()` directly. Hence, the pass
is very ineffective for even the most trivial C++ Task types. Improving
CoroElide by implementing more powerful analyses is possible, however it
doesn't give us the predictability when we expect elision to happen.
The approach we want to take with this language extension generally
originates from the philosophy that library implementations of `Task`
types has the control over the structured concurrency guarantees we
demand for elision to happen. That is, the lifetime for the callee's
frame is shorter to that of the caller.
The ``[[clang::coro_await_elidable]]`` is a class attribute which can be
applied to a coroutine return type.
When a coroutine function that returns such a type calls another
coroutine function, the compiler performs heap allocation elision when
the following conditions are all met:
- callee coroutine function returns a type that is annotated with
``[[clang::coro_await_elidable]]``.
- In caller coroutine, the return value of the callee is a prvalue that
is immediately `co_await`ed.
>From the C++ perspective, it makes sense because we can ensure the
lifetime of elided callee cannot exceed that of the caller if we can
guarantee that the caller coroutine is never destroyed earlier than the
callee coroutine. This is not generally true for any C++ programs.
However, the library that implements `Task` types and executors may
provide this guarantee to the compiler, providing the user with
certainty that HALO will work on their programs.
After this patch, when compiling coroutines that return a type with such
attribute, the frontend checks that the type of the operand of
`co_await` expressions (not `operator co_await`). If it's also
attributed with `[[clang::coro_await_elidable]]`, the FE emits metadata
on the call or invoke instruction as a hint for a later middle end pass
to elide the elision.
The original patch version is
https://github.com/llvm/llvm-project/pull/94693 and as suggested, the
patch is split into frontend and middle end solutions into stacked PRs.
The middle end CoroSplit patch can be found at
https://github.com/llvm/llvm-project/pull/99283
The middle end transformation that performs the elide can be found at
https://github.com/llvm/llvm-project/pull/99285
To unsubscribe from these emails, change your notification settings at https://github.com/llvm/llvm-project/settings/notifications
More information about the All-commits
mailing list