File frame.cpp¶
File List > modules > portable > frame.cpp
Go to the documentation of this file.
#include "coroutine/frame.h"
// #include <experimental/coroutine>
#if defined(__GNUC__)
using procedure_t = void (*)(void*);
#else
using procedure_t = void(__cdecl*)(void*);
#endif
constexpr auto align_req_v = sizeof(void*) * 2;
template <typename P>
constexpr auto aligned_size_v = (sizeof(P) + align_req_v - 1u) &
~(align_req_v - 1u);
constexpr ptrdiff_t portable_aligned_size(size_t _TypeSize) {
return (_TypeSize + align_req_v - 1u) & ~(align_req_v - 1u);
}
struct clang_frame_prefix {
procedure_t _Factivate;
procedure_t _Fdestroy;
};
//static_assert(aligned_size_v<clang_frame_prefix> == 16);
using gcc_frame_prefix = clang_frame_prefix;
// - Note
// MSVC coroutine frame's prefix
// Reference <experimental/resumable> for the detail
// - Layout
struct msvc_frame_prefix {
procedure_t _Factivate;
uint16_t _Index;
uint16_t _Flag;
};
//static_assert(aligned_size_v<msvc_frame_prefix> == 16);
//
// intrinsic: MSVC
//
extern "C" {
size_t _coro_resume(void*);
void _coro_destroy(void*);
size_t _coro_done(void*);
}
//
// intrinsic: Clang/GCC
//
//extern "C" {
template <bool B>
void resume_wrapper(void *p)
{
if constexpr (B)
__builtin_coro_resume(p);
}
template <bool B>
void destroy_wrapper(void *p)
{
if constexpr(B)
__builtin_coro_destroy(p);
}
template <bool B>
bool done_wrapper(void *p)
{
if constexpr(B)
return __builtin_coro_done(p);
return false;
}
// void* __builtin_coro_promise(void* ptr, int align, bool p);
//}
bool _coro_finished(portable_coro_prefix* _Handle);
#if defined(__clang__)
static constexpr auto is_clang = true;
static constexpr auto is_msvc = !is_clang;
struct portable_coro_prefix final : public clang_frame_prefix {};
#elif defined(_MSC_VER)
static constexpr auto is_msvc = true;
static constexpr auto is_clang = !is_msvc;
#pragma intrinsic(_coro_resume)
#pragma intrinsic(_coro_destroy)
#pragma intrinsic(_coro_done)
struct portable_coro_prefix final : public msvc_frame_prefix {};
inline bool _coro_finished(portable_coro_prefix* _Handle) {
return _Handle->_Index == 0;
}
#elif defined(__GNUC__)
// For now, work like a clang
static constexpr auto is_clang = true;
static constexpr auto is_msvc = !is_clang;
struct portable_coro_prefix final : public clang_frame_prefix {};
extern "C" {
bool __builtin_coro_is_suspended(void*);
}
#endif // __clang__ || _MSC_VER || __GNUC__
// replacement of the `_coro_done`
bool portable_coro_done(portable_coro_prefix* _Handle) {
if constexpr (is_msvc) {
return _coro_finished(_Handle);
} else if constexpr (is_clang) {
return done_wrapper<true>(_Handle);
}
return false; // follow `noop_coroutine`
}
void portable_coro_resume(portable_coro_prefix* _Handle) {
if constexpr (is_msvc) {
_coro_resume(_Handle);
} else if constexpr (is_clang) {
resume_wrapper<true>(_Handle);
}
}
void portable_coro_destroy(portable_coro_prefix* _Handle) {
if constexpr (is_msvc) {
_coro_destroy(_Handle);
} else if constexpr (is_clang) {
destroy_wrapper<true>(_Handle);
}
}
void* portable_coro_get_promise(portable_coro_prefix* _Handle,
ptrdiff_t _PromSize) {
// location of the promise object
void* _PromAddr = nullptr;
if constexpr (is_clang) {
// for Clang, promise is placed just after frame prefix
// see also: `__builtin_coro_promise`
_PromAddr = reinterpret_cast<std::byte*>(_Handle) +
aligned_size_v<clang_frame_prefix>;
} else if constexpr (is_msvc) {
// for MSVC, promise is placed before frame prefix
_PromAddr = reinterpret_cast<std::byte*>(_Handle) -
portable_aligned_size(_PromSize);
}
return _PromAddr;
}
portable_coro_prefix* portable_coro_from_promise(void* _PromAddr,
ptrdiff_t _PromSize) {
// location of the frame prefix
void* _Handle = nullptr;
if constexpr (is_clang) {
_Handle = reinterpret_cast<std::byte*>(_PromAddr) -
aligned_size_v<clang_frame_prefix>;
} else if constexpr (is_msvc) {
_Handle = reinterpret_cast<std::byte*>(_PromAddr) +
portable_aligned_size(_PromSize);
}
return reinterpret_cast<portable_coro_prefix*>(_Handle);
}