blob: ecd9515acb815e3f641c3bde2aa31892aee069e3 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
// Test for PR56919. Tests the destroy function contains the call to delete function only.
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 %s -O3 -S -o - | FileCheck %s
#include "Inputs/coroutine.h"
namespace std {
template <typename T> struct remove_reference { using type = T; };
template <typename T> struct remove_reference<T &> { using type = T; };
template <typename T> struct remove_reference<T &&> { using type = T; };
template <typename T>
constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
return static_cast<typename std::remove_reference<T>::type &&>(t);
}
}
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> Qux() { co_return; }
Task<void> Baz() { co_await Qux(); }
Task<void> Bar() { co_await Baz(); }
// CHECK: _Z3Quxv.destroy:{{.*}}
// CHECK-NEXT: #
// CHECK-NEXT: jmp _ZdlPv
// CHECK: _Z3Bazv.destroy:{{.*}}
// CHECK-NEXT: #
// CHECK-NEXT: jmp _ZdlPv
// CHECK: _Z3Barv.destroy:{{.*}}
// CHECK-NEXT: #
// CHECK-NEXT: jmp _ZdlPv
|