xref: /llvm-project/clang/test/CodeGenCoroutines/pr56919.cpp (revision 130e93cc26ca9d3ac50ec5a92e3109577ca2e702)
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)16 constexpr 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()108 Task<void> Qux() { co_return; }
Baz()109 Task<void> Baz() { co_await Qux(); }
Bar()110 Task<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