Files
clang-p2996/clang/test/CodeGenCoroutines/pr56919.cpp
Chuanqi Xu 17631ac676 [Coroutines] Store the index for final suspend point if there is unwind coro end
Closing https://github.com/llvm/llvm-project/issues/57339

The root cause for this issue is an pre-mature optimization to eliminate
the index for the final suspend point since we feel like we can judge
if a coroutine is suspended at the final suspend by if resume_fn_addr is
null. However this is not true if the coroutine exists via an exception
in promise.unhandled_exception(). According to
[dcl.fct.def.coroutine]p14:

> If the evaluation of the expression promise.unhandled_exception()
> exits via an exception, the coroutine is considered suspended at the
> final suspend point.

But from the perspective of the implementation, we can't set the coro
index to the final suspend point directly since it breaks the states.

To fix the issue, we block the optimization if we find there is any
unwind coro end, which indicates that it is possible that the coroutine
exists via an exception from promise.unhandled_exception().

Test Plan: folly
2022-08-26 14:05:46 +08:00

123 lines
3.2 KiB
C++

// Test for PR56919. Tests the destroy function contains the call to delete function only.
//
// REQUIRES: x86-registered-target
//
// 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