1 // Test for PR56919. Tests the destroy function contains the call to delete function only. 2 // 3 // REQUIRES: x86-registered-target 4 // 5 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 %s -O3 -S -o - | FileCheck %s 6 7 #include "Inputs/coroutine.h" 8 9 namespace std { 10 11 template <typename T> struct remove_reference { using type = T; }; 12 template <typename T> struct remove_reference<T &> { using type = T; }; 13 template <typename T> struct remove_reference<T &&> { using type = T; }; 14 15 template <typename T> move(T && 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 22 template <typename T> 23 class Task final { 24 public: 25 using value_type = T; 26 27 class promise_type final { 28 public: get_return_object()29 Task<void> get_return_object() { return Task<void>(std::coroutine_handle<promise_type>::from_promise(*this)); } 30 unhandled_exception()31 void unhandled_exception() {} 32 initial_suspend()33 std::suspend_always initial_suspend() { return {}; } 34 await_transform(Task<void> co)35 auto await_transform(Task<void> co) { 36 return await_transform(std::move(co.handle_.promise())); 37 } 38 await_transform(promise_type && awaited)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 coroutine 48 // 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 return_void()61 void return_void() {} 62 63 // At final suspend resume our registered handle. final_suspend()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: my_handle()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 ~Task()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 coroutine 92 // library doesn't expose handles to the user, so we know this constraint isn't 93 // violated. 94 if (!handle_.done()) { 95 __builtin_unreachable(); 96 } 97 98 handle_.destroy(); 99 } 100 101 private: Task(const std::coroutine_handle<promise_type> handle)102 explicit Task(const std::coroutine_handle<promise_type> handle) 103 : handle_(handle) {} 104 105 const std::coroutine_handle<promise_type> handle_; 106 }; 107 Qux()108Task<void> Qux() { co_return; } Baz()109Task<void> Baz() { co_await Qux(); } Bar()110Task<void> Bar() { co_await Baz(); } 111 112 // CHECK: _Z3Quxv.destroy:{{.*}} 113 // CHECK-NEXT: # 114 // CHECK-NEXT: movl $40, %esi 115 // CHECK-NEXT: jmp _ZdlPvm@PLT 116 117 // CHECK: _Z3Bazv.destroy:{{.*}} 118 // CHECK-NEXT: # 119 // CHECK-NEXT: movl $80, %esi 120 // CHECK-NEXT: jmp _ZdlPvm 121 122 // CHECK: _Z3Barv.destroy:{{.*}} 123 // CHECK-NEXT: # 124 // CHECK-NEXT: movl $120, %esi 125 // CHECK-NEXT: jmp _ZdlPvm 126