From ec0f62d7899c762978670329bec973e1d808a461 Mon Sep 17 00:00:00 2001 From: couriersud Date: Mon, 8 Jun 2020 03:11:41 +0200 Subject: [PATCH] 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 --- src/lib/netlist/plib/ppmf.h | 59 ++++++++++++++++-------- src/lib/netlist/plib/ptypes.h | 84 +++++++++++++++++++++++++++++++++++ src/lib/netlist/plib/putil.h | 18 ++++++++ 3 files changed, 143 insertions(+), 18 deletions(-) diff --git a/src/lib/netlist/plib/ppmf.h b/src/lib/netlist/plib/ppmf.h index f83e4620861..919897df37f 100644 --- a/src/lib/netlist/plib/ppmf.h +++ b/src/lib/netlist/plib/ppmf.h @@ -33,6 +33,8 @@ /// #include "pconfig.h" +#include "ptypes.h" +#include "putil.h" #include #include // 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 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(reinterpret_cast(object) + m_this_delta); + generic_class *o_p_delta = reinterpret_cast(reinterpret_cast(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), "size mismatch 2"); - *reinterpret_cast *>(&m_resolved) = r.first; + //*reinterpret_cast *>(&m_resolved) = r.first; + reinterpret_copy(r.first, m_resolved); m_obj = reinterpret_cast(r.second); } @@ -357,8 +381,7 @@ namespace plib { }; template - using pmfp = pmfp_base; - + using pmfp = pmfp_base; /// /// \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 diff --git a/src/lib/netlist/plib/ptypes.h b/src/lib/netlist/plib/ptypes.h index c40735a0f48..ef3dd724388 100644 --- a/src/lib/netlist/plib/ptypes.h +++ b/src/lib/netlist/plib/ptypes.h @@ -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; + using version = std::integral_constant; + #elif defined(__GNUC__) + using type = std::integral_constant; + using version = std::integral_constant; + #elif defined(_MSC_VER) + using type = std::integral_constant; + using version = std::integral_constant; + #else + using type = std::integral_constant; + using version = std::integral_constant; + #endif + #ifdef __unix__ + using is_unix = std::integral_constant; + #else + using is_unix = std::integral_constant; + #endif + #if defined(__linux__) + using os = std::integral_constant; + #elif defined(__FreeBSD__) + using os = std::integral_constant; + #elif defined(__OpenBSD__) + using os = std::integral_constant; + #elif defined(__EMSCRIPTEN__) + using os = std::integral_constant; + #elif defined(_WIN32) + using os = std::integral_constant; + #elif defined(__APPLE__) + using os = std::integral_constant; + #else + using os = std::integral_constant; + #endif + #if defined(__x86_64__) || defined (_M_X64) || defined(__aarch64__) || defined(__mips64) || defined(_M_AMD64) || defined(_M_ARM64) + using m64 = std::integral_constant; + #else + using m64 = std::integral_constant; + #endif + #if defined(__x86_64__) || defined(__i386__) + using arch = std::integral_constant; + #elif defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(_M_ARM) + using arch = std::integral_constant; + #elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) + using arch = std::integral_constant; + #else + using arch = std::integral_constant; + #endif + #if defined(__MINGW32__) + using mingw = std::integral_constant; + #else + using mingw = std::integral_constant; + #endif }; + } // namespace plib using INT128 = plib::compile_info::int128_type; diff --git a/src/lib/netlist/plib/putil.h b/src/lib/netlist/plib/putil.h index 20ea01e890e..8ceec3d7151 100644 --- a/src/lib/netlist/plib/putil.h +++ b/src/lib/netlist/plib/putil.h @@ -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 + void reinterpret_copy(S &s, D &d) + { + static_assert(sizeof(D) >= sizeof(S), "size mismatch"); + auto *dp = reinterpret_cast(&d); + const auto *sp = reinterpret_cast(&s); + std::copy(sp, sp + sizeof(S), dp); + } + namespace util { pstring basename(const pstring &filename, const pstring &suffix = "");