diff options
-rw-r--r-- | src/devices/cpu/drcbex64.cpp | 8 | ||||
-rw-r--r-- | src/emu/emumem_aspace.cpp | 54 | ||||
-rw-r--r-- | src/lib/util/mfpresolve.cpp | 201 | ||||
-rw-r--r-- | src/lib/util/mfpresolve.h | 72 |
4 files changed, 267 insertions, 68 deletions
diff --git a/src/devices/cpu/drcbex64.cpp b/src/devices/cpu/drcbex64.cpp index 413f6465f63..facc81da91c 100644 --- a/src/devices/cpu/drcbex64.cpp +++ b/src/devices/cpu/drcbex64.cpp @@ -2530,7 +2530,7 @@ void drcbe_x64::op_read(Assembler &a, const instruction &inst) // set up a call to the read handler auto const &accessors = m_memory_accessors[spacesizep.space()]; bool const have_specific = (uintptr_t(nullptr) != accessors.specific.read.function) || accessors.specific.read.is_virtual; - auto const addr_mask = make_bitmask<uint32_t>(accessors.specific.address_width) & ~make_bitmask<uint32_t>(accessors.specific.native_mask_bits); + offs_t const addr_mask = m_space[spacesizep.space()]->addrmask() & make_bitmask<offs_t>(accessors.specific.address_width) & ~make_bitmask<offs_t>(accessors.specific.native_mask_bits); mov_reg_param(a, Gpd(REG_PARAM2), addrp); if (have_specific && ((1 << spacesizep.size()) == accessors.specific.native_bytes)) { @@ -2698,7 +2698,7 @@ void drcbe_x64::op_readm(Assembler &a, const instruction &inst) // set up a call to the read handler auto const &accessors = m_memory_accessors[spacesizep.space()]; bool const have_specific = (uintptr_t(nullptr) != accessors.specific.read.function) || accessors.specific.read.is_virtual; - auto const addr_mask = make_bitmask<uint32_t>(accessors.specific.address_width) & ~make_bitmask<uint32_t>(accessors.specific.native_mask_bits); + offs_t const addr_mask = m_space[spacesizep.space()]->addrmask() & make_bitmask<offs_t>(accessors.specific.address_width) & ~make_bitmask<offs_t>(accessors.specific.native_mask_bits); mov_reg_param(a, Gpd(REG_PARAM2), addrp); if (spacesizep.size() != SIZE_QWORD) mov_reg_param(a, Gpd(REG_PARAM3), maskp); @@ -2856,7 +2856,7 @@ void drcbe_x64::op_write(Assembler &a, const instruction &inst) // set up a call to the write handler auto const &accessors = m_memory_accessors[spacesizep.space()]; bool const have_specific = (uintptr_t(nullptr) != accessors.specific.write.function) || accessors.specific.write.is_virtual; - auto const addr_mask = make_bitmask<uint32_t>(accessors.specific.address_width) & ~make_bitmask<uint32_t>(accessors.specific.native_mask_bits); + offs_t const addr_mask = m_space[spacesizep.space()]->addrmask() & make_bitmask<offs_t>(accessors.specific.address_width) & ~make_bitmask<offs_t>(accessors.specific.native_mask_bits); mov_reg_param(a, Gpd(REG_PARAM2), addrp); if (spacesizep.size() != SIZE_QWORD) mov_reg_param(a, Gpd(REG_PARAM3), srcp); @@ -2986,7 +2986,7 @@ void drcbe_x64::op_writem(Assembler &a, const instruction &inst) // set up a call to the write handler auto const &accessors = m_memory_accessors[spacesizep.space()]; bool const have_specific = (uintptr_t(nullptr) != accessors.specific.write.function) || accessors.specific.write.is_virtual; - auto const addr_mask = make_bitmask<uint32_t>(accessors.specific.address_width) & ~make_bitmask<uint32_t>(accessors.specific.native_mask_bits); + offs_t const addr_mask = m_space[spacesizep.space()]->addrmask() & make_bitmask<offs_t>(accessors.specific.address_width) & ~make_bitmask<offs_t>(accessors.specific.native_mask_bits); mov_reg_param(a, Gpd(REG_PARAM2), addrp); if (spacesizep.size() != SIZE_QWORD) mov_reg_param(a, Gpd(REG_PARAM3), srcp); diff --git a/src/emu/emumem_aspace.cpp b/src/emu/emumem_aspace.cpp index 513a3e33463..f93472b110b 100644 --- a/src/emu/emumem_aspace.cpp +++ b/src/emu/emumem_aspace.cpp @@ -10,10 +10,6 @@ ***************************************************************************/ #include "emu.h" -#include <list> -#include <map> -#include "emuopts.h" -#include "debug/debugcpu.h" #include "emumem_mud.h" #include "emumem_hea.h" @@ -27,6 +23,14 @@ #include "emumem_het.h" #include "emumem_hws.h" +#include "emuopts.h" +#include "debug/debugcpu.h" + +#include "mfpresolve.h" + +#include <list> +#include <map> + //************************************************************************** // DEBUGGING @@ -321,49 +325,11 @@ public: accessors.low_bits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); accessors.read.dispatch = reinterpret_cast<void const *const *>(m_dispatch_read); accessors.write.dispatch = reinterpret_cast<void const *const *>(m_dispatch_write); - accessors.read.function = accessors.write.function = uintptr_t(nullptr); - accessors.read.displacement = accessors.write.displacement = 0; - accessors.read.is_virtual = accessors.write.is_virtual = false; auto readfunc = &handler_entry_read<Width, AddrShift>::read; auto writefunc = &handler_entry_write<Width, AddrShift>::write; - if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM) { - struct { std::uintptr_t ptr; std::ptrdiff_t adj; } equiv; - constexpr uintptr_t funcmask = ~uintptr_t((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 0 : 1); - constexpr unsigned deltashift = (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0; - - assert(sizeof(readfunc) == sizeof(equiv)); - *reinterpret_cast<decltype(readfunc) *>(&equiv) = readfunc; - accessors.read.function = equiv.ptr & funcmask; - accessors.read.displacement = equiv.adj >> deltashift; - accessors.read.is_virtual = BIT((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? equiv.adj : equiv.ptr, 0); - - assert(sizeof(writefunc) == sizeof(equiv)); - *reinterpret_cast<decltype(writefunc) *>(&equiv) = writefunc; - accessors.write.function = equiv.ptr & funcmask; - accessors.write.displacement = equiv.adj >> deltashift; - accessors.write.is_virtual = BIT((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? equiv.adj : equiv.ptr, 0); - } else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) { - struct single { std::uintptr_t entrypoint; }; - struct multi { std::uintptr_t entrypoint; int this_delta; }; - struct { std::uintptr_t entrypoint; int this_delta; int vptr_offs; int vt_index; } const *unknown; - - assert(sizeof(*unknown) >= sizeof(readfunc)); - unknown = reinterpret_cast<decltype(unknown)>(&readfunc); - if ((sizeof(*unknown) > sizeof(readfunc)) || !unknown->vt_index) { - accessors.read.function = unknown->entrypoint; - accessors.read.displacement = (sizeof(single) < sizeof(readfunc)) ? unknown->this_delta : 0; - accessors.read.is_virtual = false; - } - - assert(sizeof(*unknown) >= sizeof(writefunc)); - unknown = reinterpret_cast<decltype(unknown)>(&writefunc); - if ((sizeof(*unknown) > sizeof(writefunc)) || !unknown->vt_index) { - accessors.write.function = unknown->entrypoint; - accessors.write.displacement = (sizeof(single) < sizeof(writefunc)) ? unknown->this_delta : 0; - accessors.write.is_virtual = false; - } - } + std::tie(accessors.read.function, accessors.read.displacement, accessors.read.is_virtual) = util::resolve_member_function(readfunc); + std::tie(accessors.write.function, accessors.write.displacement, accessors.write.is_virtual) = util::resolve_member_function(writefunc); return accessors; } diff --git a/src/lib/util/mfpresolve.cpp b/src/lib/util/mfpresolve.cpp index 4542a29229d..c7b9c58fde2 100644 --- a/src/lib/util/mfpresolve.cpp +++ b/src/lib/util/mfpresolve.cpp @@ -62,36 +62,47 @@ std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_itanium( } +std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function_itanium( + std::uintptr_t function, + std::ptrdiff_t delta) noexcept +{ + constexpr uintptr_t funcmask = ~uintptr_t((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 0 : 1); + constexpr int deltashift = (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0; + return std::make_tuple( + function & funcmask, + delta >> deltashift, + (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? (delta & 1) : (function & 1)); +} + + std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_msvc( void const *funcptr, std::size_t size, void const *object) noexcept { - struct single { std::uintptr_t entrypoint; }; - struct multi { std::uintptr_t entrypoint; int this_delta; }; - struct { std::uintptr_t entrypoint; int this_delta; int vptr_offs; int vt_index; } const *unknown; + mfp_msvc_unknown_equiv const *unknown; assert(sizeof(*unknown) >= size); - unknown = reinterpret_cast<decltype(unknown)>(funcptr); + unknown = reinterpret_cast<mfp_msvc_unknown_equiv const *>(funcptr); LOG("Input this=%p ", object); - if (sizeof(single) < size) - LOG("thisdelta=%d ", unknown->this_delta); - if (sizeof(*unknown) == size) - LOG("vptrdelta=%d vindex=%d ", unknown->vptr_offs, unknown->vt_index); + if (sizeof(mfp_msvc_single_equiv) < size) + LOG("thisdelta=%d ", unknown->delta); + if (sizeof(mfp_msvc_unknown_equiv) == size) + LOG("vptrdelta=%d vindex=%d ", unknown->voffset, unknown->vindex); auto byteptr = reinterpret_cast<std::uint8_t const *>(object); // test for pointer to member function cast across virtual inheritance relationship - if ((sizeof(*unknown) == size) && unknown->vt_index) + if ((sizeof(mfp_msvc_unknown_equiv) == size) && unknown->vindex) { // add offset from "this" pointer to location of vptr, and add offset to virtual base from vtable - byteptr += unknown->vptr_offs; - std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr); - byteptr += *reinterpret_cast<int const *>(vptr + unknown->vt_index); + byteptr += unknown->voffset; + auto const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr); + byteptr += *reinterpret_cast<int const *>(vptr + unknown->vindex); } // add "this" pointer displacement if present in the pointer to member function - if (sizeof(single) < size) - byteptr += unknown->this_delta; + if (sizeof(mfp_msvc_single_equiv) < size) + byteptr += unknown->delta; LOG("Calculated this=%p\n", reinterpret_cast<void const *>(byteptr)); // walk past recognisable thunks @@ -99,6 +110,26 @@ std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_msvc( } +std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function_msvc( + void const *funcptr, + std::size_t size) noexcept +{ + mfp_msvc_unknown_equiv const *unknown; + assert(sizeof(*unknown) >= size); + unknown = reinterpret_cast<mfp_msvc_unknown_equiv const *>(funcptr); + + // no way to represent pointer to member function cast across virtual inheritance relationship + if ((sizeof(mfp_msvc_unknown_equiv) == size) && unknown->vindex) + return std::make_tuple(std::uintptr_t(static_cast<void (*)()>(nullptr)), std::ptrdiff_t(0), false); + + auto const [function, is_virtual] = bypass_member_function_thunks(unknown->entrypoint); + return std::make_tuple( + function, + (sizeof(mfp_msvc_single_equiv) < size) ? unknown->delta : 0, + is_virtual); +} + + std::uintptr_t bypass_member_function_thunks( std::uintptr_t entrypoint, void const *object) noexcept @@ -255,4 +286,146 @@ std::uintptr_t bypass_member_function_thunks( #endif } + +std::pair<std::uintptr_t, bool> bypass_member_function_thunks( + std::uintptr_t entrypoint) noexcept +{ +#if defined(__x86_64__) || defined(_M_X64) + std::uint8_t const *func = reinterpret_cast<std::uint8_t const *>(entrypoint); + while (true) + { + // Assumes Windows calling convention, and doesn't consider that + // the "this" pointer could be in RDX if RCX is a pointer to + // space for an oversize scalar result. Since the result area + // is uninitialised on entry, you won't see something that looks + // like a vtable dispatch through RCX in this case - it won't + // behave badly, it just won't bypass virtual call thunks in the + // rare situations where the return type is an oversize scalar. + if (0xe9 == func[0]) + { + // relative jump with 32-bit displacement (typically a resolved PLT entry) + LOG("Found relative jump at %p ", func); + func += std::ptrdiff_t(5) + *reinterpret_cast<std::int32_t const *>(func + 1); + LOG("redirecting to %p\n", func); + continue; + } + else if ((0x48 == func[0]) && (0x8b == func[1]) && (0x01 == func[2])) + { + if ((0xff == func[3]) && ((0x20 == func[4]) || (0x60 == func[4]) || (0xa0 == func[4]))) + { + // MSVC virtual function call thunk - mov rax,QWORD PTR [rcx] ; jmp QWORD PTR [rax+...] + LOG("Found virtual member function thunk at %p\n", func); + if (0x20 == func[4]) // no displacement + return std::make_pair(std::uintptr_t(0), true); + else if (0x60 == func[4]) // 8-bit displacement + return std::make_pair(std::uintptr_t(*reinterpret_cast<std::int8_t const *>(func + 5)), true); + else // 32-bit displacement + return std::make_pair(std::uintptr_t(*reinterpret_cast<std::int32_t const *>(func + 5)), true); + } + else if ((0x48 == func[3]) && (0x8b == func[4])) + { + // clang virtual function call thunk - mov rax,QWORD PTR [rcx] ; mov rax,QWORD PTR [rax+...] ; jmp rax + if ((0x00 == func[5]) && (0x48 == func[6]) && (0xff == func[7]) && (0xe0 == func[8])) + { + // no displacement + LOG("Found virtual member function thunk at %p\n", func); + return std::make_pair(std::uintptr_t(0), true); + } + else if ((0x40 == func[5]) && (0x48 == func[7]) && (0xff == func[8]) && (0xe0 == func[9])) + { + // 8-bit displacement + LOG("Found virtual member function thunk at %p\n", func); + return std::make_pair(std::uintptr_t(*reinterpret_cast<std::int8_t const *>(func + 6)), true); + } + else if ((0x80 == func[5]) && (0x48 == func[10]) && (0xff == func[11]) && (0xe0 == func[12])) + { + // 32-bit displacement + LOG("Found virtual member function thunk at %p\n", func); + return std::make_pair(std::uintptr_t(*reinterpret_cast<std::int32_t const *>(func + 6)), true); + } + } + } + + // clang uses unoptimised thunks if optimisation is disabled + // Without optimisation, clang produces thunks like: + // 50 push rax + // 48 89 0c 24 mov QWORD PTR [rsp],rcx + // 48 8b 0c 24 mov rcx,QWORD PTR [rsp] + // 48 8b 01 mov rax,QWORD PTR [rcx] + // 48 8b 80 xx xx xx xx mov rax,QWORD PTR [rax+...] + // 41 5a pop r10 + // 48 ff e0 jmp rax + // Trying to decode these thunks likely isn't worth the effort. + // Chasing performance in unoptimised builds isn't very useful, + // and the format of these thunks may be fragile. + + // not something we can easily bypass + break; + } + return std::make_pair(std::uintptr_t(func), false); +#elif defined(__aarch64__) || defined(_M_ARM64) + std::uint32_t const *func = reinterpret_cast<std::uint32_t const *>(entrypoint); + auto const fetch = [&func] (auto offset) { return little_endianize_int32(func[offset]); }; + while (true) + { + if ((0x90000010 == (fetch(0) & 0x9f00001f)) && (0x91000210 == (fetch(1) & 0xffc003ff)) && (0xd61f0200 == fetch(2))) + { + // page-relative jump with +/-4GB reach - adrp xip0,... ; add xip0,xip0,#... ; br xip0 + LOG("Found page-relative jump at %p ", func); + std::int64_t const page = + (std::uint64_t(fetch(0) & 0x60000000) >> 17) | + (std::uint64_t(fetch(0) & 0x00ffffe0) << 9) | + ((fetch(0) & 0x00800000) ? (~std::uint64_t(0) << 33) : 0); + std::uint32_t const offset = (fetch(1) & 0x003ffc00) >> 10; + func = reinterpret_cast<std::uint32_t const *>(((std::uintptr_t(func) + page) & (~std::uintptr_t(0) << 12)) + offset); + LOG("redirecting to %p\n", func); + } + else if ((0xf9400010 == fetch(0)) && (0xf9400210 == (fetch(1) & 0xffc003ff)) && (0xd61f0200 == fetch(2))) + { + // virtual function call thunk - ldr xip0,[x0] ; ldr xip0,[x0,#...] ; br xip0 + LOG("Found virtual member function thunk at %p\n", func); + return std::make_pair(std::uintptr_t((fetch(1) & 0x003ffc00) >> (10 - 3)), true); + } + else + { + // not something we can easily bypass + break; + } + + // clang uses horribly sub-optimal thunks for AArch64 + // Without optimisation, clang produces thunks like: + // d10143ff sub sp,sp,#80 + // f90027e7 str x7,[sp,#72] + // f90023e6 str x6,[sp,#64] + // f9001fe5 str x5,[sp,#56] + // f9001be4 str x4,[sp,#48] + // f90017e3 str x3,[sp,#40] + // f90013e2 str x2,[sp,#32] + // f9000fe1 str x1,[sp,#24] + // f90007e0 str x0,[sp,#8] + // f94007e0 ldr x0,[sp,#8] + // f9400009 ldr x9,[x0] + // f9400129 ldr x9,[x9,#...] + // 910143ff add sp,sp,#80 + // d61f0120 br x9 + // With optimisation, clang produces thunks like: + // d10103ff sub sp,sp,#64 + // a9008be1 stp x1,x2,[sp,#8] + // a90193e3 stp x3,x4,[sp,#24] + // a9029be5 stp x5,x6,[sp,#40] + // f9001fe7 str x7,[sp,#56] + // f9400009 ldr x9,[x0] + // f9400129 ldr x9,[x9,#...] + // 910103ff add sp,sp,#64 + // d61f0120 br x9 + // It's more effort than it's worth to try decoding these + // thunks. + + } + return std::make_pair(std::uintptr_t(func), false); +#else + return std::make_pair(entrypoint, false); +#endif +} + } // namespace util::detail diff --git a/src/lib/util/mfpresolve.h b/src/lib/util/mfpresolve.h index 7661623572a..3c67cd543ff 100644 --- a/src/lib/util/mfpresolve.h +++ b/src/lib/util/mfpresolve.h @@ -17,6 +17,7 @@ #include <cassert> #include <cstddef> #include <cstdint> +#include <tuple> #include <utility> @@ -24,9 +25,26 @@ namespace util { namespace detail { +struct mfp_itanium_equiv +{ + std::uintptr_t function; + std::ptrdiff_t delta; + + constexpr std::ptrdiff_t this_delta() const noexcept { return delta >> ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0); } + constexpr bool is_virtual() const noexcept { return ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? delta : function) & 1; } +}; + +struct mfp_msvc_single_equiv { std::uintptr_t entrypoint; }; +struct mfp_msvc_multi_equiv { std::uintptr_t entrypoint; int delta; }; +struct mfp_msvc_virtual_equiv { std::uintptr_t entrypoint; int delta; int vindex; }; +struct mfp_msvc_unknown_equiv { std::uintptr_t entrypoint; int delta; int voffset; int vindex; }; + std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_itanium(std::uintptr_t function, std::ptrdiff_t delta, void const *object) noexcept; +std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function_itanium(std::uintptr_t function, std::ptrdiff_t delta) noexcept; std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_msvc(void const *funcptr, std::size_t size, void const *object) noexcept; +std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function_msvc(void const *funcptr, std::size_t size) noexcept; std::uintptr_t bypass_member_function_thunks(std::uintptr_t entrypoint, void const *object) noexcept; +std::pair<std::uintptr_t, bool> bypass_member_function_thunks(std::uintptr_t entrypoint) noexcept; } // namespace detail @@ -46,10 +64,10 @@ inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T: { if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM) { - struct { std::uintptr_t ptr; std::ptrdiff_t adj; } equiv; + detail::mfp_itanium_equiv equiv; assert(sizeof(function) == sizeof(equiv)); *reinterpret_cast<decltype(function) *>(&equiv) = function; - return detail::resolve_member_function_itanium(equiv.ptr, equiv.adj, &object); + return detail::resolve_member_function_itanium(equiv.function, equiv.delta, &object); } else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) { @@ -57,7 +75,7 @@ inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T: } else { - return std::make_pair(std::uintptr_t(nullptr), std::uintptr_t(nullptr)); + return std::make_pair(std::uintptr_t(static_cast<void (*)()>(nullptr)), std::uintptr_t(nullptr)); } } @@ -67,10 +85,10 @@ inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T: { if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM) { - struct { std::uintptr_t ptr; std::ptrdiff_t adj; } equiv; + detail::mfp_itanium_equiv equiv; assert(sizeof(function) == sizeof(equiv)); *reinterpret_cast<decltype(function) *>(&equiv) = function; - return detail::resolve_member_function_itanium(equiv.ptr, equiv.adj, &object); + return detail::resolve_member_function_itanium(equiv.function, equiv.delta, &object); } else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) { @@ -78,7 +96,49 @@ inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T: } else { - return std::make_pair(std::uintptr_t(nullptr), std::uintptr_t(nullptr)); + return std::make_pair(std::uintptr_t(static_cast<void (*)()>(nullptr)), std::uintptr_t(nullptr)); + } +} + + +template <typename T, typename Ret, typename... Params> +inline std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function(Ret (T::*function)(Params...)) noexcept +{ + if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM) + { + detail::mfp_itanium_equiv equiv; + assert(sizeof(function) == sizeof(equiv)); + *reinterpret_cast<decltype(function) *>(&equiv) = function; + return detail::resolve_member_function_itanium(equiv.function, equiv.delta); + } + else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) + { + return detail::resolve_member_function_msvc(&function, sizeof(function)); + } + else + { + return std::make_tuple(std::uintptr_t(static_cast<void (*)()>(nullptr)), std::ptrdiff_t(0), false); + } +} + + +template <typename T, typename Ret, typename... Params> +inline std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function(Ret (T::*function)(Params...) const) noexcept +{ + if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM) + { + detail::mfp_itanium_equiv equiv; + assert(sizeof(function) == sizeof(equiv)); + *reinterpret_cast<decltype(function) *>(&equiv) = function; + return detail::resolve_member_function_itanium(equiv.function, equiv.delta); + } + else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) + { + return detail::resolve_member_function_msvc(&function, sizeof(function)); + } + else + { + return std::make_tuple(std::uintptr_t(static_cast<void (*)()>(nullptr)), std::ptrdiff_t(0), false); } } |