mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 21:16:05 +00:00

Since C++14 has been released for about nine years and most standard libraries have implemented sized deallocation functions, it's time to make this feature default again. This is another try of https://reviews.llvm.org/D112921. The original commit cf5a8b4 was reverted by 2e5035a due to some failures (see #83774). Fixes #60061
126 lines
3.3 KiB
C++
126 lines
3.3 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: movl $40, %esi
|
|
// CHECK-NEXT: jmp _ZdlPvm@PLT
|
|
|
|
// CHECK: _Z3Bazv.destroy:{{.*}}
|
|
// CHECK-NEXT: #
|
|
// CHECK-NEXT: movl $80, %esi
|
|
// CHECK-NEXT: jmp _ZdlPvm
|
|
|
|
// CHECK: _Z3Barv.destroy:{{.*}}
|
|
// CHECK-NEXT: #
|
|
// CHECK-NEXT: movl $120, %esi
|
|
// CHECK-NEXT: jmp _ZdlPvm
|