netlist: further optimize ppmf.h [Couriersud]

Move towards a pure c++ constexpr solution to derive the optimal
pointer to member function solution.

All the macro madness is centralized to a compile_info struct with
static members.

For the time being the evaluation result is compared against the
previous approach. Going forward this will be dropped as well as the
support for MINGW with GCC <= 4.6
This commit is contained in:
couriersud 2020-06-08 03:11:41 +02:00
parent d041e74e2c
commit ec0f62d789
3 changed files with 143 additions and 18 deletions

View File

@ -33,6 +33,8 @@
///
#include "pconfig.h"
#include "ptypes.h"
#include "putil.h"
#include <algorithm>
#include <cstdint> // uintptr_t
@ -48,15 +50,18 @@
#define PPMF_TYPE_GNUC_PMF_CONV 1
#define PPMF_TYPE_INTERNAL 2
#if defined(__GNUC__)
// FIXME: Remove this macro madmess latest after September, 2020
// FIXME: Do we still need to support MINGW <= 4.6?
#if defined(__clang__) && defined(__i386__) && defined(_WIN32)
#define PHAS_PMF_INTERNAL 0
#elif defined(__GNUC__) || defined(__clang__)
// does not work in versions over 4.7.x of 32bit MINGW
#if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)))
#define PHAS_PMF_INTERNAL 0
#elif defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
#define PHAS_PMF_INTERNAL 1
#define MEMBER_ABI _thiscall
#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
#define PHAS_PMF_INTERNAL 0
#elif defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) || defined(__EMSCRIPTEN__)
#define PHAS_PMF_INTERNAL 2
#else
@ -91,22 +96,40 @@
#endif
#endif
#if defined(__GNUC__) || defined(__clang__)
#if defined(__GNUC__)
#pragma GCC diagnostic push
#endif
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wundefined-reinterpret-cast"
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
#if (PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV)
#pragma GCC diagnostic ignored "-Wpmf-conversions"
#endif
namespace plib {
struct ppmf_internal
{
using ci = compile_info;
enum { value =
(ci::type() == ci_compiler::CLANG && !ci::m64()
&& ci::os() == ci_os::WINDOWS) ? 0 :
(ci::mingw() && !ci::m64() && ci::version() >= 407) ? 0 :
(ci::mingw() && !ci::m64()) ? 1 :
((ci::type() == ci_compiler::CLANG || ci::type() == ci_compiler::GCC)
&& (ci::arch() == ci_arch::MIPS
|| ci::arch() == ci_arch::ARM
|| ci::os() == ci_os::EMSCRIPTEN)) ? 2 :
(ci::type() == ci_compiler::CLANG || ci::type() == ci_compiler::GCC) ? 1 :
(ci::type() == ci_compiler::MSC && ci::m64()) ? 3 :
0
};
};
// FIXME: on supported platforms we should consider using GNU PMF extensions
// if no internal solution exists
using ppmf_type = std::integral_constant<int, (ppmf_internal::value > 0 ? PPMF_TYPE_INTERNAL : PPMF_TYPE_PMF)>;
// check against previous implementation
static_assert(ppmf_internal::value == PHAS_PMF_INTERNAL, "internal mismatch");
static_assert(ppmf_type::value == PPMF_TYPE, "type mismatch");
///
/// \brief Used to derive a pointer to a member function.
///
@ -137,7 +160,7 @@ namespace plib {
{
// apply the "this" delta to the object first
// NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
auto *o_p_delta = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
generic_class *o_p_delta = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
// if the low bit of the vtable index is clear, then it is just a raw function pointer
if ((m_function & 1) == 0)
@ -335,7 +358,8 @@ namespace plib {
static_assert(sizeof(m_resolved) >= sizeof(r.first), "size mismatch 1");
static_assert(sizeof(m_resolved) >= sizeof(member_abi_function<O>), "size mismatch 2");
*reinterpret_cast<member_abi_function<O> *>(&m_resolved) = r.first;
//*reinterpret_cast<member_abi_function<O> *>(&m_resolved) = r.first;
reinterpret_copy(r.first, m_resolved);
m_obj = reinterpret_cast<generic_class *>(r.second);
}
@ -357,8 +381,7 @@ namespace plib {
};
template<typename R, typename... Targs>
using pmfp = pmfp_base<PPMF_TYPE, PHAS_PMF_INTERNAL, R, Targs...>;
using pmfp = pmfp_base<ppmf_type::value, ppmf_internal::value, R, Targs...>;
///
/// \brief Class to support delegate late binding
@ -423,7 +446,7 @@ namespace plib {
} // namespace plib
#if defined(__GNUC__) || defined(__clang__)
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

View File

@ -40,9 +40,40 @@ namespace plib
//============================================================
/// \brief Dummy 128 bit types for platforms which don't support 128 bit
///
/// Users should always consult compile_info::has_int128 prior to
/// using the UINT128/INT128 data type.
///
struct UINT128_DUMMY {};
struct INT128_DUMMY {};
enum class ci_compiler
{
UNKNOWN,
CLANG,
GCC,
MSC
};
enum class ci_os
{
UNKNOWN,
LINUX,
FREEBSD,
OPENBSD,
WINDOWS,
MACOSX,
EMSCRIPTEN
};
enum class ci_arch
{
UNKNOWN,
X86,
ARM,
MIPS
};
struct compile_info
{
#ifdef _WIN32
@ -69,7 +100,60 @@ namespace plib
static constexpr int128_type int128_max() { return int128_type(); }
static constexpr uint128_type uint128_max() { return uint128_type(); }
#endif
#ifdef __clang__
using type = std::integral_constant<ci_compiler, ci_compiler::CLANG>;
using version = std::integral_constant<int, (__clang_major__) * 100 + (__clang_minor__)>;
#elif defined(__GNUC__)
using type = std::integral_constant<ci_compiler, ci_compiler::GCC>;
using version = std::integral_constant<int, (__GNUC__) * 100 + (__GNUC_MINOR__)>;
#elif defined(_MSC_VER)
using type = std::integral_constant<ci_compiler, ci_compiler::MSC>;
using version = std::integral_constant<int, _MSC_VER>;
#else
using type = std::integral_constant<ci_compiler, ci_compiler::UNKNOWN>;
using version = std::integral_constant<int, 0>;
#endif
#ifdef __unix__
using is_unix = std::integral_constant<bool, true>;
#else
using is_unix = std::integral_constant<bool, false>;
#endif
#if defined(__linux__)
using os = std::integral_constant<ci_os, ci_os::LINUX>;
#elif defined(__FreeBSD__)
using os = std::integral_constant<ci_os, ci_os::FREEBSD>;
#elif defined(__OpenBSD__)
using os = std::integral_constant<ci_os, ci_os::OPENBSD>;
#elif defined(__EMSCRIPTEN__)
using os = std::integral_constant<ci_os, ci_os::EMSCRIPTEN>;
#elif defined(_WIN32)
using os = std::integral_constant<ci_os, ci_os::WINDOWS>;
#elif defined(__APPLE__)
using os = std::integral_constant<ci_os, ci_os::MACOSX>;
#else
using os = std::integral_constant<ci_os, ci_os::UNKNOWN>;
#endif
#if defined(__x86_64__) || defined (_M_X64) || defined(__aarch64__) || defined(__mips64) || defined(_M_AMD64) || defined(_M_ARM64)
using m64 = std::integral_constant<bool, true>;
#else
using m64 = std::integral_constant<bool, false>;
#endif
#if defined(__x86_64__) || defined(__i386__)
using arch = std::integral_constant<ci_arch, ci_arch::X86>;
#elif defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(_M_ARM)
using arch = std::integral_constant<ci_arch, ci_arch::ARM>;
#elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
using arch = std::integral_constant<ci_arch, ci_arch::MIPS>;
#else
using arch = std::integral_constant<ci_arch, ci_arch::UNKNOWN>;
#endif
#if defined(__MINGW32__)
using mingw = std::integral_constant<bool, true>;
#else
using mingw = std::integral_constant<bool, false>;
#endif
};
} // namespace plib
using INT128 = plib::compile_info::int128_type;

View File

@ -249,6 +249,24 @@ namespace plib
list_t m_collection;
};
/// \brief copy type S to type D byte by byte
///
/// The purpose of this copy function is to suppress compiler warnings.
/// Use at your own risk. This is dangerous.
///
/// \param s Source object
/// \param d Destination object
/// \tparam S Type of source object
/// \tparam D Type of destination object
template <typename S, typename D>
void reinterpret_copy(S &s, D &d)
{
static_assert(sizeof(D) >= sizeof(S), "size mismatch");
auto *dp = reinterpret_cast<std::uint8_t *>(&d);
const auto *sp = reinterpret_cast<std::uint8_t *>(&s);
std::copy(sp, sp + sizeof(S), dp);
}
namespace util
{
pstring basename(const pstring &filename, const pstring &suffix = "");