Skip to content

Commit bf5f235

Browse files
committed
[NFC] [Coroutines] Add regression tests for symmetric transfer and coroutine elision
1 parent de05128 commit bf5f235

File tree

3 files changed

+176
-10
lines changed

3 files changed

+176
-10
lines changed

clang/test/CodeGenCoroutines/Inputs/coroutine.h

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// This is a mock file for <coroutine>.
12
#pragma once
23

34
namespace std {
@@ -52,20 +53,54 @@ template <typename Promise> struct coroutine_handle : coroutine_handle<> {
5253
}
5354
};
5455

55-
template <typename _PromiseT>
56-
bool operator==(coroutine_handle<_PromiseT> const& _Left,
57-
coroutine_handle<_PromiseT> const& _Right) noexcept
58-
{
59-
return _Left.address() == _Right.address();
56+
template <typename _PromiseT>
57+
bool operator==(coroutine_handle<_PromiseT> const &_Left,
58+
coroutine_handle<_PromiseT> const &_Right) noexcept {
59+
return _Left.address() == _Right.address();
60+
}
61+
62+
template <typename _PromiseT>
63+
bool operator!=(coroutine_handle<_PromiseT> const &_Left,
64+
coroutine_handle<_PromiseT> const &_Right) noexcept {
65+
return !(_Left == _Right);
66+
}
67+
68+
struct noop_coroutine_promise {};
69+
70+
template <>
71+
struct coroutine_handle<noop_coroutine_promise> {
72+
operator coroutine_handle<>() const noexcept {
73+
return coroutine_handle<>::from_address(address());
6074
}
6175

62-
template <typename _PromiseT>
63-
bool operator!=(coroutine_handle<_PromiseT> const& _Left,
64-
coroutine_handle<_PromiseT> const& _Right) noexcept
65-
{
66-
return !(_Left == _Right);
76+
constexpr explicit operator bool() const noexcept { return true; }
77+
constexpr bool done() const noexcept { return false; }
78+
79+
constexpr void operator()() const noexcept {}
80+
constexpr void resume() const noexcept {}
81+
constexpr void destroy() const noexcept {}
82+
83+
noop_coroutine_promise &promise() const noexcept {
84+
return *static_cast<noop_coroutine_promise *>(
85+
__builtin_coro_promise(this->__handle_, alignof(noop_coroutine_promise), false));
6786
}
6887

88+
constexpr void *address() const noexcept { return __handle_; }
89+
90+
private:
91+
friend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept;
92+
93+
coroutine_handle() noexcept {
94+
this->__handle_ = __builtin_coro_noop();
95+
}
96+
97+
void *__handle_ = nullptr;
98+
};
99+
100+
using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
101+
102+
inline noop_coroutine_handle noop_coroutine() noexcept { return noop_coroutine_handle(); }
103+
69104
struct suspend_always {
70105
bool await_ready() noexcept { return false; }
71106
void await_suspend(coroutine_handle<>) noexcept {}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// This tests that the coroutine elide optimization could happen succesfully.
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O2 -emit-llvm %s -o - | FileCheck %s
3+
4+
#include "Inputs/coroutine.h"
5+
6+
struct Task {
7+
struct promise_type {
8+
struct FinalAwaiter {
9+
bool await_ready() const noexcept { return false; }
10+
template <typename PromiseType>
11+
std::coroutine_handle<> await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
12+
if (!h)
13+
return std::noop_coroutine();
14+
return h.promise().continuation;
15+
}
16+
void await_resume() noexcept {}
17+
};
18+
Task get_return_object() noexcept {
19+
return std::coroutine_handle<promise_type>::from_promise(*this);
20+
}
21+
std::suspend_always initial_suspend() noexcept { return {}; }
22+
FinalAwaiter final_suspend() noexcept { return {}; }
23+
void unhandled_exception() noexcept {}
24+
void return_value(int x) noexcept {
25+
_value = x;
26+
}
27+
std::coroutine_handle<> continuation;
28+
int _value;
29+
};
30+
31+
Task(std::coroutine_handle<promise_type> handle) : handle(handle) {}
32+
~Task() {
33+
if (handle)
34+
handle.destroy();
35+
}
36+
37+
struct Awaiter {
38+
bool await_ready() const noexcept { return false; }
39+
void await_suspend(std::coroutine_handle<void> continuation) noexcept {}
40+
int await_resume() noexcept {
41+
return 43;
42+
}
43+
};
44+
45+
auto operator co_await() {
46+
return Awaiter{};
47+
}
48+
49+
private:
50+
std::coroutine_handle<promise_type> handle;
51+
};
52+
53+
Task task0() {
54+
co_return 43;
55+
}
56+
57+
Task task1() {
58+
co_return co_await task0();
59+
}
60+
61+
// CHECK: %_Z5task1v.Frame = type {{.*}}%_Z5task0v.Frame
62+
// CHECK-LABEL: define{{.*}} void @_Z5task1v.resume
63+
// CHECK-NOT: call{{.*}}_Znwm
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// This tests that the symmetric transfer at the final suspend point could happen successfully.
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -O2 -emit-llvm %s -o - | FileCheck %s
3+
4+
#include "Inputs/coroutine.h"
5+
6+
struct Task {
7+
struct promise_type {
8+
struct FinalAwaiter {
9+
bool await_ready() const noexcept { return false; }
10+
template <typename PromiseType>
11+
std::coroutine_handle<> await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
12+
return h.promise().continuation;
13+
}
14+
void await_resume() noexcept {}
15+
};
16+
Task get_return_object() noexcept {
17+
return std::coroutine_handle<promise_type>::from_promise(*this);
18+
}
19+
std::suspend_always initial_suspend() noexcept { return {}; }
20+
FinalAwaiter final_suspend() noexcept { return {}; }
21+
void unhandled_exception() noexcept {}
22+
void return_value(int x) noexcept {
23+
_value = x;
24+
}
25+
std::coroutine_handle<> continuation;
26+
int _value;
27+
};
28+
29+
Task(std::coroutine_handle<promise_type> handle) : handle(handle) {}
30+
~Task() {
31+
if (handle)
32+
handle.destroy();
33+
}
34+
35+
struct Awaiter {
36+
std::coroutine_handle<promise_type> handle;
37+
Awaiter(std::coroutine_handle<promise_type> handle) : handle(handle) {}
38+
bool await_ready() const noexcept { return false; }
39+
std::coroutine_handle<void> await_suspend(std::coroutine_handle<void> continuation) noexcept {
40+
handle.promise().continuation = continuation;
41+
return handle;
42+
}
43+
int await_resume() noexcept {
44+
int ret = handle.promise()._value;
45+
handle.destroy();
46+
return ret;
47+
}
48+
};
49+
50+
auto operator co_await() {
51+
auto handle_ = handle;
52+
handle = nullptr;
53+
return Awaiter(handle_);
54+
}
55+
56+
private:
57+
std::coroutine_handle<promise_type> handle;
58+
};
59+
60+
Task task0() {
61+
co_return 43;
62+
}
63+
64+
// CHECK-LABEL: define{{.*}} void @_Z5task0v.resume
65+
// This checks we are still in the scope of the current function.
66+
// CHECK-NOT: {{^}}}
67+
// CHECK: musttail call fastcc void
68+
// CHECK-NEXT: ret void

0 commit comments

Comments
 (0)