126 lines · cpp
1// Test for PR56919. Tests the destroy function contains the call to delete function only.2//3// REQUIRES: x86-registered-target4//5// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 %s -O3 -S -o - | FileCheck %s6 7#include "Inputs/coroutine.h"8 9namespace std {10 11template <typename T> struct remove_reference { using type = T; };12template <typename T> struct remove_reference<T &> { using type = T; };13template <typename T> struct remove_reference<T &&> { using type = T; };14 15template <typename T>16constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {17 return static_cast<typename std::remove_reference<T>::type &&>(t);18}19 20}21 22template <typename T>23class Task final {24 public:25 using value_type = T;26 27 class promise_type final {28 public: 29 Task<void> get_return_object() { return Task<void>(std::coroutine_handle<promise_type>::from_promise(*this)); }30 31 void unhandled_exception() {}32 33 std::suspend_always initial_suspend() { return {}; }34 35 auto await_transform(Task<void> co) {36 return await_transform(std::move(co.handle_.promise()));37 }38 39 auto await_transform(promise_type&& awaited) {40 struct Awaitable {41 promise_type&& awaited;42 43 bool await_ready() { return false; }44 45 std::coroutine_handle<> await_suspend(46 const std::coroutine_handle<> handle) {47 // Register our handle to be resumed once the awaited promise's coroutine48 // finishes, and then resume that coroutine.49 awaited.registered_handle_ = handle;50 return std::coroutine_handle<promise_type>::from_promise(awaited);51 }52 53 void await_resume() {}54 55 private:56 };57 58 return Awaitable{std::move(awaited)};59 }60 61 void return_void() {}62 63 // At final suspend resume our registered handle.64 auto final_suspend() noexcept {65 struct FinalSuspendAwaitable final {66 bool await_ready() noexcept { return false; }67 68 std::coroutine_handle<> await_suspend(69 std::coroutine_handle<> h) noexcept {70 return to_resume;71 }72 73 void await_resume() noexcept {}74 75 std::coroutine_handle<> to_resume;76 };77 78 return FinalSuspendAwaitable{registered_handle_};79 }80 81 private:82 std::coroutine_handle<promise_type> my_handle() {83 return std::coroutine_handle<promise_type>::from_promise(*this);84 }85 86 std::coroutine_handle<> registered_handle_;87 };88 89 ~Task() {90 // Teach llvm that we are only ever destroyed when the coroutine body is done,91 // so there is no need for the jump table in the destroy function. Our coroutine92 // library doesn't expose handles to the user, so we know this constraint isn't93 // violated.94 if (!handle_.done()) {95 __builtin_unreachable();96 }97 98 handle_.destroy();99 }100 101 private:102 explicit Task(const std::coroutine_handle<promise_type> handle)103 : handle_(handle) {}104 105 const std::coroutine_handle<promise_type> handle_;106};107 108Task<void> Qux() { co_return; }109Task<void> Baz() { co_await Qux(); }110Task<void> Bar() { co_await Baz(); }111 112// CHECK: _Z3Quxv.destroy:{{.*}}113// CHECK-NEXT: #114// CHECK-NEXT: movl $40, %esi115// CHECK-NEXT: jmp _ZdlPvm@PLT116 117// CHECK: _Z3Bazv.destroy:{{.*}}118// CHECK-NEXT: #119// CHECK-NEXT: movl $80, %esi120// CHECK-NEXT: jmp _ZdlPvm121 122// CHECK: _Z3Barv.destroy:{{.*}}123// CHECK-NEXT: #124// CHECK-NEXT: movl $120, %esi125// CHECK-NEXT: jmp _ZdlPvm126