blob: f935e256d9db99fc5ec9bc5e95ebbb31c4ce1557 [file] [log] [blame]
// 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