<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">