<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/56919>56919</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Clang generates unnecessary code in coroutine destroy function
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
jacobsa
</td>
</tr>
</table>
<pre>
The following program contains a basic coroutine task type, whose destructor destroys the coroutine handle. It uses `__builtin_unreachable` to teach llvm that it will only ever be destroyed when the coroutine is done (this is a requirement of the coroutine library).
```c++
#include <coroutine>
#include <utility>
template <typename T>
class Task final {
public:
using value_type = T;
class promise_type final {
public:
Task<void> get_return_object() { return Task<void>(std::coroutine_handle<promise_type>::from_promise(*this)); }
void unhandled_exception();
std::suspend_always initial_suspend() { return {}; }
auto await_transform(Task<void> co) {
return await_transform(std::move(co.handle_.promise()));
}
auto await_transform(promise_type&& awaited) {
struct Awaitable {
promise_type&& awaited;
bool await_ready() { return false; }
std::coroutine_handle<> await_suspend(
const std::coroutine_handle<> handle) {
// Register our handle to be resumed once the awaited promise's coroutine
// finishes, and then resume that coroutine.
awaited.registered_handle_ = handle;
return std::coroutine_handle<promise_type>::from_promise(awaited);
}
void await_resume() {}
private:
};
return Awaitable{std::move(awaited)};
}
void return_void() {}
// At final suspend resume our registered handle.
auto final_suspend() noexcept {
struct FinalSuspendAwaitable final {
bool await_ready() noexcept { return false; }
std::coroutine_handle<> await_suspend(
std::coroutine_handle<> h) noexcept {
return to_resume;
}
void await_resume() noexcept {}
std::coroutine_handle<> to_resume;
};
return FinalSuspendAwaitable{registered_handle_};
}
private:
std::coroutine_handle<promise_type> my_handle() {
return std::coroutine_handle<promise_type>::from_promise(*this);
}
std::coroutine_handle<> registered_handle_;
};
~Task() {
// Teach llvm that we are only ever destroyed when the coroutine body is done,
// so there is no need for the jump table in the destroy function. Our coroutine
// library doesn't expose handles to the user, so we know this constraint isn't
// violated.
if (!handle_.done()) {
__builtin_unreachable();
}
handle_.destroy();
}
private:
explicit Task(const std::coroutine_handle<promise_type> handle)
: handle_(handle) {}
const std::coroutine_handle<promise_type> handle_;
};
Task<void> __attribute__((noinline)) Qux() { co_return; }
Task<void> Baz() { co_await Qux(); }
Task<void> __attribute__((noinline)) Bar() { co_await Baz(); }
```
When compiled with `-O2 -std=c++20 -fno-exceptions` using trunk clang ([compiler explorer](https://godbolt.org/z/bjbdbE6nG)), the following code is generated for `Bar`'s destroy function for x86-64:
```
Bar() [clone .destroy]:
cmp qword ptr [rdi], 0
je .LBB9_2
cmp byte ptr [rdi + 80], 0
.LBB9_2:
jmp operator delete(void*)@PLT
```
**There seems to be a missed optimization here**: all of the instructions besides `jmp` can be omitted without changing the semantics. The other instructions seem to be a remnant of logic for conditionally destroying the frames for `Baz` and `Qux`, but that is never necessary because `Bar` is only ever destroyed once it's done (so the frames for `Baz` and `Qux` have already been destroyed).
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJy9WG1v4jgQ_jXwZVSUBgrlAx9Kuz2dtNK9VbqPkZMYcNexWdspZX_9zdhOAiFt93ZPhxAvif3MeOZ5xuPkujyunnYcNlpKfRBqC3ujt4ZVUGjlmFAWGOTMigIvGF07oTg4Zr-AO-75KL2Hw05bDiW3ztSF0yb81EcLDnG7STumSskn8KuD2nILo3mSZXktJN7NamU4K3Yslxyvg9Pg6D9I-VIhDnMgHByElKCVPAJ_4QZy3pjiJXrBVc-gsFBq_B6lt26HfwQtxfCvtTC84sqB3vRmSJEbZo6jdDkZJQ-j5C5-zpPwLkbpmt7hajoVqpB1iRam9y3IaPpp6D7ek8Idu7v-0_FqL5nzIyieilUcntpBhWTWwhNFeyMUkzBaROOwr3MpitE0AgHGlJL3wmTNM4JCyAeCWp_aAwiQmONK2DiwDw0dOLSXvBfo5YsWJfoHW-4yw11tVKbzZ144jDLGjWAgXO_NwPvWleTw9K4NVhZIgaNOPaLRftwGL2bxjse_o0SiGXpP12js4Xx1AGQNahVwy4y_FnzvhFbBvYtwALRO2dpiAsqMyQND8golnGAyi5cvl0cBQ_vDbrAaKcwOTLjMGabsRpsKIXpBLHTE7CZCA385ufW00i8UjkJPwjKzyWmMlm18OtTv9_AsD-kc32EQLwdcDZKHOxpA0u3fB3gH7jIV9Mq1ltEtrAjl8TLuGyZxpcNhP0voAMso6AH8JK1ns-mFlc-6D3Hin8uo-ICnj_iGP_lWWIe1StcmTqDahpXLcFtXWLe0KrivQjEs0KZyYbvK9AY8KlfYHbdUhhGccFREDkWzBZj0EaK5iYkeolYimXzhaFZ6sbCYhJ-VcsepCxtvpNULuyEGrbBjxvCMvREvWFpPSmSDPsS8uK6Wygjb19uJzycgb7js3Y0F0sv9PW9jOu9crMWRnE0qiTxdnpqNtCdlP7NXrZQO9e8N3T7SlL_CjE7Dl9vBe8I8NfH_KfRDbb63-pN0O92Q6SdZeGbrx1b9li8fMHYwiejFpbA_Zu2QZP6F0qE6Nvc6tl96_B-2AR-I8KOQD8SoQxyK-2jxyW_hA8uLGn7qta0HrOyGn3St77asObbjTd-KRf0C3WqaYHxvqzQojjC4cXuU57raQ5CwCLjRFGxqVVAPNIHfsJIMbCoRPba_aJ1bbJgWDvjrnpr7EBzr23LExfbd0JaD3uDyvih9AN9g-33T4JkBu_UAcGHiRWjqd8uT6iU24ON53TQzYfGxk-lzaPjIcN7cvcGGFj-E5WJSf8qlGjAc2BfjSSSS4ONGoa-Qtmk4Uzg22g3_0tvzvqK_jB812TH7kte9rjTLmHNG5LXjWeajdKu0UJIoE3LyR_160pcVOp4Ezkp-D3TNvp1P8UW0Q3pv7scOrZkZQm-Nnm9GzXHuNAZ_kxgLXe2FJGkKt6MD6tVvKVz5YD_Es1-awNVG6av2YGHpvBoOX7irqi90wsLfZPdmHQGNZ442KJubB0qxc3tLCfSi2Ooy19JNtNniP_T4MX_Oy_zTXP3SdPP3XnfdGb3Qpa8BW664IT35KoCeUCBwYdQ89tXvh7zezq_ms5bSgwE5CSauQNIRutUMuj_t7WsFlh16fT1og-2rMzTNlMIv9R6S89HPPHxPPq_XyywdhsqPeCTukDCYa7hNzgEbgL47zxFD7yky_nGE5I5KRGjD7iigs-T3z0_vsMEPu3vypdZyXtnYtTNAaVlq2zH3lfjGfGBpWJhBSmb0kCI8WRAqdFpEE5xuRRmee6CPxJqCKQJFuToXSYdiBqxpauv5tCPrFVNOFHYC9JhGU_k_xyX_WvcMrxQLjzak3orCJx1rRiloMLp2bHjRGNgYVqFXLX--kWd0mMBvUiex6R5Qe_ExDG48fh9TvODW0naR84LhltDRjwYNbXj-rCNcYGd8MhN2tO_wAuvYC65Q-uYTbaJcW2R6YDPmq-v5zXKe3Mym6bhcTcvldMnGTjjJV_dek41aLNSqcz9oSZ3swn3ljGsjVz3NYq7qfIL6xj-028evK6y-4WnIIxKlpqPZ4818eb0c71aLokxu5zwtypvpJpmht9fFDVvwxbJcsE1SjiXLubQrJP0oTRU_gIfA38j8sVilSZomt8ksWU6T6_nkZjYryzm_5km-nLHFNZIauSLkhPygYjI2K-9SXm8t3pTY6tjuJrNWbLGB8OYQHw8RO21Wz6zQuWVjb3rlXf8Hdqce4A">