mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-30 19:16:06 +00:00
208 lines
7.5 KiB
C++
208 lines
7.5 KiB
C++
![]() |
// Tests that we can mark await-suspend as noinline correctly.
|
||
|
//
|
||
|
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s \
|
||
|
// RUN: -disable-llvm-passes | FileCheck %s
|
||
|
|
||
|
#include "Inputs/coroutine.h"
|
||
|
|
||
|
struct Task {
|
||
|
struct promise_type {
|
||
|
struct FinalAwaiter {
|
||
|
bool await_ready() const noexcept { return false; }
|
||
|
template <typename PromiseType>
|
||
|
std::coroutine_handle<> await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
|
||
|
return h.promise().continuation;
|
||
|
}
|
||
|
void await_resume() noexcept {}
|
||
|
};
|
||
|
|
||
|
Task get_return_object() noexcept {
|
||
|
return std::coroutine_handle<promise_type>::from_promise(*this);
|
||
|
}
|
||
|
|
||
|
std::suspend_always initial_suspend() noexcept { return {}; }
|
||
|
FinalAwaiter final_suspend() noexcept { return {}; }
|
||
|
void unhandled_exception() noexcept {}
|
||
|
void return_void() noexcept {}
|
||
|
|
||
|
std::coroutine_handle<> continuation;
|
||
|
};
|
||
|
|
||
|
Task(std::coroutine_handle<promise_type> handle);
|
||
|
~Task();
|
||
|
|
||
|
private:
|
||
|
std::coroutine_handle<promise_type> handle;
|
||
|
};
|
||
|
|
||
|
struct StatefulAwaiter {
|
||
|
int value;
|
||
|
bool await_ready() const noexcept { return false; }
|
||
|
template <typename PromiseType>
|
||
|
void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {}
|
||
|
void await_resume() noexcept {}
|
||
|
};
|
||
|
|
||
|
typedef std::suspend_always NoStateAwaiter;
|
||
|
using AnotherStatefulAwaiter = StatefulAwaiter;
|
||
|
|
||
|
template <class T>
|
||
|
struct TemplatedAwaiter {
|
||
|
T value;
|
||
|
bool await_ready() const noexcept { return false; }
|
||
|
template <typename PromiseType>
|
||
|
void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {}
|
||
|
void await_resume() noexcept {}
|
||
|
};
|
||
|
|
||
|
|
||
|
class Awaitable {};
|
||
|
StatefulAwaiter operator co_await(Awaitable) {
|
||
|
return StatefulAwaiter{};
|
||
|
}
|
||
|
|
||
|
StatefulAwaiter GlobalAwaiter;
|
||
|
class Awaitable2 {};
|
||
|
StatefulAwaiter& operator co_await(Awaitable2) {
|
||
|
return GlobalAwaiter;
|
||
|
}
|
||
|
|
||
|
Task testing() {
|
||
|
co_await std::suspend_always{};
|
||
|
co_await StatefulAwaiter{};
|
||
|
co_await AnotherStatefulAwaiter{};
|
||
|
|
||
|
// Test lvalue case.
|
||
|
StatefulAwaiter awaiter;
|
||
|
co_await awaiter;
|
||
|
|
||
|
// The explicit call to await_suspend is not considered suspended.
|
||
|
awaiter.await_suspend(std::coroutine_handle<void>::from_address(nullptr));
|
||
|
|
||
|
co_await TemplatedAwaiter<int>{};
|
||
|
TemplatedAwaiter<int> TemplatedAwaiterInstace;
|
||
|
co_await TemplatedAwaiterInstace;
|
||
|
|
||
|
co_await Awaitable{};
|
||
|
co_await Awaitable2{};
|
||
|
}
|
||
|
|
||
|
// CHECK-LABEL: @_Z7testingv
|
||
|
|
||
|
// Check `co_await __promise__.initial_suspend();` Since it returns std::suspend_always,
|
||
|
// which is an empty class, we shouldn't generate optimization blocker for it.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE{{.*}}#[[NORMAL_ATTR:[0-9]+]]
|
||
|
|
||
|
// Check the `co_await std::suspend_always{};` expression. We shouldn't emit the optimization
|
||
|
// blocker for it since it is an empty class.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE{{.*}}#[[NORMAL_ATTR]]
|
||
|
|
||
|
// Check `co_await StatefulAwaiter{};`. We need to emit the optimization blocker since
|
||
|
// the awaiter is not empty.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZN15StatefulAwaiter13await_suspendIN4Task12promise_typeEEEvSt16coroutine_handleIT_E{{.*}}#[[NOINLINE_ATTR:[0-9]+]]
|
||
|
|
||
|
// Check `co_await AnotherStatefulAwaiter{};` to make sure that we can handle TypedefTypes.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZN15StatefulAwaiter13await_suspendIN4Task12promise_typeEEEvSt16coroutine_handleIT_E{{.*}}#[[NOINLINE_ATTR]]
|
||
|
|
||
|
// Check `co_await awaiter;` to make sure we can handle lvalue cases.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZN15StatefulAwaiter13await_suspendIN4Task12promise_typeEEEvSt16coroutine_handleIT_E{{.*}}#[[NOINLINE_ATTR]]
|
||
|
|
||
|
// Check `awaiter.await_suspend(...)` to make sure the explicit call the await_suspend won't be marked as noinline
|
||
|
// CHECK: call void @_ZN15StatefulAwaiter13await_suspendIvEEvSt16coroutine_handleIT_E{{.*}}#[[NORMAL_ATTR]]
|
||
|
|
||
|
// Check `co_await TemplatedAwaiter<int>{};` to make sure we can handle specialized template
|
||
|
// type.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZN16TemplatedAwaiterIiE13await_suspendIN4Task12promise_typeEEEvSt16coroutine_handleIT_E{{.*}}#[[NOINLINE_ATTR]]
|
||
|
|
||
|
// Check `co_await TemplatedAwaiterInstace;` to make sure we can handle the lvalue from
|
||
|
// specialized template type.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZN16TemplatedAwaiterIiE13await_suspendIN4Task12promise_typeEEEvSt16coroutine_handleIT_E{{.*}}#[[NOINLINE_ATTR]]
|
||
|
|
||
|
// Check `co_await Awaitable{};` to make sure we can handle awaiter returned by
|
||
|
// `operator co_await`;
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZN15StatefulAwaiter13await_suspendIN4Task12promise_typeEEEvSt16coroutine_handleIT_E{{.*}}#[[NOINLINE_ATTR]]
|
||
|
|
||
|
// Check `co_await Awaitable2{};` to make sure we can handle awaiter returned by
|
||
|
// `operator co_await` which returns a reference;
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call void @_ZN15StatefulAwaiter13await_suspendIN4Task12promise_typeEEEvSt16coroutine_handleIT_E{{.*}}#[[NOINLINE_ATTR]]
|
||
|
|
||
|
// Check `co_await __promise__.final_suspend();`. We don't emit an blocker here since it is
|
||
|
// empty.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK: call ptr @_ZN4Task12promise_type12FinalAwaiter13await_suspendIS0_EESt16coroutine_handleIvES3_IT_E{{.*}}#[[NORMAL_ATTR]]
|
||
|
|
||
|
struct AwaitTransformTask {
|
||
|
struct promise_type {
|
||
|
struct FinalAwaiter {
|
||
|
bool await_ready() const noexcept { return false; }
|
||
|
template <typename PromiseType>
|
||
|
std::coroutine_handle<> await_suspend(std::coroutine_handle<PromiseType> h) noexcept {
|
||
|
return h.promise().continuation;
|
||
|
}
|
||
|
void await_resume() noexcept {}
|
||
|
};
|
||
|
|
||
|
AwaitTransformTask get_return_object() noexcept {
|
||
|
return std::coroutine_handle<promise_type>::from_promise(*this);
|
||
|
}
|
||
|
|
||
|
std::suspend_always initial_suspend() noexcept { return {}; }
|
||
|
FinalAwaiter final_suspend() noexcept { return {}; }
|
||
|
void unhandled_exception() noexcept {}
|
||
|
void return_void() noexcept {}
|
||
|
|
||
|
template <typename Awaitable>
|
||
|
auto await_transform(Awaitable &&awaitable) {
|
||
|
return awaitable;
|
||
|
}
|
||
|
|
||
|
std::coroutine_handle<> continuation;
|
||
|
};
|
||
|
|
||
|
AwaitTransformTask(std::coroutine_handle<promise_type> handle);
|
||
|
~AwaitTransformTask();
|
||
|
|
||
|
private:
|
||
|
std::coroutine_handle<promise_type> handle;
|
||
|
};
|
||
|
|
||
|
struct awaitableWithGetAwaiter {
|
||
|
bool await_ready() const noexcept { return false; }
|
||
|
template <typename PromiseType>
|
||
|
void await_suspend(std::coroutine_handle<PromiseType> h) noexcept {}
|
||
|
void await_resume() noexcept {}
|
||
|
};
|
||
|
|
||
|
AwaitTransformTask testingWithAwaitTransform() {
|
||
|
co_await awaitableWithGetAwaiter{};
|
||
|
}
|
||
|
|
||
|
// CHECK-LABEL: @_Z25testingWithAwaitTransformv
|
||
|
|
||
|
// Init suspend
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK-NOT: call void @llvm.coro.opt.blocker(
|
||
|
// CHECK: call void @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE{{.*}}#[[NORMAL_ATTR]]
|
||
|
|
||
|
// Check `co_await awaitableWithGetAwaiter{};`.
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK-NOT: call void @llvm.coro.opt.blocker(
|
||
|
// Check call void @_ZN23awaitableWithGetAwaiter13await_suspendIN18AwaitTransformTask12promise_typeEEEvSt16coroutine_handleIT_E{{.*}}#[[NORMAL_ATTR]]
|
||
|
|
||
|
// Final suspend
|
||
|
// CHECK: call token @llvm.coro.save
|
||
|
// CHECK-NOT: call void @llvm.coro.opt.blocker(
|
||
|
// CHECK: call ptr @_ZN18AwaitTransformTask12promise_type12FinalAwaiter13await_suspendIS0_EESt16coroutine_handleIvES3_IT_E{{.*}}#[[NORMAL_ATTR]]
|
||
|
|
||
|
// CHECK-NOT: attributes #[[NORMAL_ATTR]] = noinline
|
||
|
// CHECK: attributes #[[NOINLINE_ATTR]] = {{.*}}noinline
|